diff --git a/.github/workflows/build-nabla.yml b/.github/workflows/build-nabla.yml index 3e8e0b4dd0..b664527558 100644 --- a/.github/workflows/build-nabla.yml +++ b/.github/workflows/build-nabla.yml @@ -129,16 +129,31 @@ jobs: --preset ci-build-dynamic-${{ matrix.vendor }} ` -t run-compiler-explorer --config ${{ matrix.config }} - - name: Container – Install NSC + - name: Container – Build Examples + id: build-examples + continue-on-error: true + run: | + docker exec orphan ` + ${{ env.entry }} ${{ env.cmd }} -Command cmake --build ` + --preset ci-build-dynamic-${{ matrix.vendor }} ` + -t examples_tests\all --config ${{ matrix.config }} ` + -- -k 0 + + - name: Container – Install Nabla run: | docker exec orphan ` ${{ env.entry }} ${{ env.cmd }} -Command cmake --install ` ${{ env.binary }} --config ${{ matrix.config }} ` - --component Runtimes --prefix ${{ env.install }} + --prefix ${{ env.install }} + + - name: Container – Install Examples + id: install-examples + continue-on-error: true + run: | docker exec orphan ` ${{ env.entry }} ${{ env.cmd }} -Command cmake --install ` - ${{ env.binary }} --config ${{ matrix.config }} ` - --component Executables --prefix ${{ env.install }} + ${{ env.binary }}\examples_tests --config ${{ matrix.config }} ` + --prefix ${{ env.install }} - name: Container – Save NSC Image run: | diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 5bd2d6859f..b242904db1 100755 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -520,6 +520,8 @@ nbl_adjust_definitions() add_custom_target(3rdparty) add_dependencies(3rdparty ${NBL_3RDPARTY_TARGETS}) +NBL_ADJUST_FOLDERS(3rdaprty) + nbl_install_dir("${CMAKE_CURRENT_SOURCE_DIR}/parallel-hashmap/parallel_hashmap") # parent scope exports, must be at the end of the file diff --git a/CMakeLists.txt b/CMakeLists.txt index c6664f8085..4e29839399 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,7 +181,7 @@ option(NBL_PCH "Enable pre-compiled header" ON) option(NBL_FAST_MATH "Enable fast low-precision math" ON) option(NBL_BUILD_EXAMPLES "Enable building examples" ON) option(NBL_BUILD_MITSUBA_LOADER "Enable nbl::ext::MitsubaLoader?" OFF) # TODO: once it compies turn this ON by default! -option(NBL_BUILD_IMGUI "Enable nbl::ext::ImGui?" OFF) +option(NBL_BUILD_IMGUI "Enable nbl::ext::ImGui?" ON) option(NBL_BUILD_OPTIX "Enable nbl::ext::OptiX?" OFF) if(NBL_COMPILE_WITH_CUDA) @@ -271,7 +271,7 @@ add_subdirectory(src/nbl) add_subdirectory("${NBL_PYTHON_MODULE_ROOT_PATH}" tests) # Python Framework if(NBL_BUILD_EXAMPLES) file(LOCK "${CMAKE_CURRENT_SOURCE_DIR}/examples_tests" DIRECTORY GUARD PROCESS RESULT_VARIABLE NBL_LOCK TIMEOUT 60) - add_subdirectory(examples_tests) + add_subdirectory(examples_tests EXCLUDE_FROM_ALL) file(LOCK "${CMAKE_CURRENT_SOURCE_DIR}/examples_tests" DIRECTORY RELEASE RESULT_VARIABLE NBL_LOCK) endif() add_subdirectory(tools) diff --git a/cmake/common.cmake b/cmake/common.cmake index 69a0a5b980..bb34e7979c 100755 --- a/cmake/common.cmake +++ b/cmake/common.cmake @@ -16,131 +16,50 @@ include_guard(GLOBAL) include(ProcessorCount) -function(nbl_handle_dll_definitions _TARGET_ _SCOPE_) - if(NOT TARGET Nabla) - message(FATAL_ERROR "Internal error, Nabla target must be defined!") - endif() - - if(NOT TARGET ${_TARGET_}) - message(FATAL_ERROR "Internal error, requsted \"${_TARGET_}\" is not defined!") - endif() - - if(NBL_COMPILER_DYNAMIC_RUNTIME) - set(_NABLA_OUTPUT_DIR_ "${NBL_ROOT_PATH_BINARY}/src/nbl/$/devshgraphicsprogramming.nabla") - - target_compile_definitions(${_TARGET_} ${_SCOPE_} - _NABLA_DLL_NAME_="$>";_NABLA_OUTPUT_DIR_="${_NABLA_OUTPUT_DIR_}" - ) - endif() - - target_compile_definitions(${_TARGET_} ${_SCOPE_} - _DXC_DLL_="${DXC_DLL}" - ) -endfunction() - -function(nbl_handle_runtime_lib_properties _TARGET_) - if(NOT TARGET ${_TARGET_}) - message(FATAL_ERROR "Internal error, requsted \"${_TARGET_}\" is not defined!") - endif() - - set_target_properties(${_TARGET_} PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>$<$:DLL>") -endfunction() - # Macro creating project for an executable # Project and target get its name from directory when this macro gets executed (truncating number in the beginning of the name and making all lower case) # Created because of common cmake code for examples and tools macro(nbl_create_executable_project _EXTRA_SOURCES _EXTRA_OPTIONS _EXTRA_INCLUDES _EXTRA_LIBS) get_filename_component(_NBL_PROJECT_DIRECTORY_ "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) - include("scripts/nbl/projectTargetName") # sets EXECUTABLE_NAME - - if(MSVC) - set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT ${EXECUTABLE_NAME}) - endif() + get_filename_component(EXECUTABLE_NAME ${_NBL_PROJECT_DIRECTORY_} NAME) + string(REGEX REPLACE "^[0-9]+\." "" EXECUTABLE_NAME ${EXECUTABLE_NAME}) + string(TOLOWER ${EXECUTABLE_NAME} EXECUTABLE_NAME) + string(MAKE_C_IDENTIFIER ${EXECUTABLE_NAME} EXECUTABLE_NAME) project(${EXECUTABLE_NAME}) + set_directory_properties(PROPERTIES VS_STARTUP_PROJECT ${EXECUTABLE_NAME}) + + set(NBL_EXECUTABLE_SOURCES + main.cpp + ${_EXTRA_SOURCES} + ) if(ANDROID) - add_library(${EXECUTABLE_NAME} SHARED main.cpp ${_EXTRA_SOURCES}) + add_library(${EXECUTABLE_NAME} SHARED ${NBL_EXECUTABLE_SOURCES}) else() - set(NBL_EXECUTABLE_SOURCES - main.cpp - ${_EXTRA_SOURCES} - ) - add_executable(${EXECUTABLE_NAME} ${NBL_EXECUTABLE_SOURCES}) - nbl_handle_runtime_lib_properties(${EXECUTABLE_NAME}) endif() - - nbl_handle_dll_definitions(${EXECUTABLE_NAME} PUBLIC) target_compile_definitions(${EXECUTABLE_NAME} PUBLIC _NBL_APP_NAME_="${EXECUTABLE_NAME}") - - if("${EXECUTABLE_NAME}" STREQUAL commonpch) - add_dependencies(${EXECUTABLE_NAME} Nabla) - else() - string(FIND "${_NBL_PROJECT_DIRECTORY_}" "${NBL_ROOT_PATH}/examples_tests" _NBL_FOUND_) - - if(NOT "${_NBL_FOUND_}" STREQUAL "-1") # the call was made for a target defined in examples_tests, request common api PCH - if(NOT TARGET ${NBL_EXECUTABLE_COMMON_API_TARGET}) - message(FATAL_ERROR "Internal error, NBL_EXECUTABLE_COMMON_API_TARGET target must be defined to create an example target!") - endif() - - add_dependencies(${EXECUTABLE_NAME} ${NBL_EXECUTABLE_COMMON_API_TARGET}) - target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${NBL_EXECUTABLE_COMMON_API_TARGET}) - target_precompile_headers("${EXECUTABLE_NAME}" REUSE_FROM "${NBL_EXECUTABLE_COMMON_API_TARGET}") - endif() - endif() target_include_directories(${EXECUTABLE_NAME} PUBLIC "${NBL_ROOT_PATH}/examples_tests/common" - PUBLIC "${NBL_ROOT_PATH_BINARY}/include" - PUBLIC ../../include # in macro.. relative to what? TODO: correct PRIVATE ${_EXTRA_INCLUDES} ) target_link_libraries(${EXECUTABLE_NAME} PUBLIC Nabla ${_EXTRA_LIBS}) - add_compile_options(${_EXTRA_OPTIONS}) - - if(NBL_SANITIZE_ADDRESS) - if(MSVC) - target_compile_options(${EXECUTABLE_NAME} PUBLIC /fsanitize=address) - else() - target_compile_options(${EXECUTABLE_NAME} PUBLIC -fsanitize=address) - endif() - endif() - - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # add_compile_options("-msse4.2 -mfpmath=sse") ???? - add_compile_options( - "$<$:-fstack-protector-all>" - ) - - set(COMMON_LINKER_OPTIONS "-msse4.2 -mfpmath=sse -fuse-ld=gold") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${COMMON_LINKER_OPTIONS}") - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${COMMON_LINKER_OPTIONS} -fstack-protector-strong") - if (NBL_GCC_SANITIZE_ADDRESS) - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=address") - endif() - if (NBL_GCC_SANITIZE_THREAD) - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=thread") - endif() - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.1) - add_compile_options(-Wno-error=ignored-attributes) - endif() - endif() - nbl_adjust_flags(TARGET ${EXECUTABLE_NAME} MAP_RELEASE Release MAP_RELWITHDEBINFO RelWithDebInfo MAP_DEBUG Debug) - nbl_adjust_definitions() # macro defined in root CMakeLists - add_definitions(-D_NBL_PCH_IGNORE_PRIVATE_HEADERS) + nbl_adjust_definitions() - set_target_properties(${EXECUTABLE_NAME} PROPERTIES DEBUG_POSTFIX _d) - set_target_properties(${EXECUTABLE_NAME} PROPERTIES RELWITHDEBINFO_POSTFIX _rwdi) - set_target_properties(${EXECUTABLE_NAME} - PROPERTIES + add_compile_options(${_EXTRA_OPTIONS}) + add_definitions(-D_NBL_PCH_IGNORE_PRIVATE_HEADERS) # TODO: wipe when we finally make Nabla PCH work as its supposed to + set_target_properties(${EXECUTABLE_NAME} PROPERTIES + DEBUG_POSTFIX _d + RELWITHDEBINFO_POSTFIX _rwdi RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_SOURCE_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin" - VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" # for visual studio + VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" ) if(MSVC) # nothing special @@ -248,66 +167,28 @@ macro(nbl_create_executable_project _EXTRA_SOURCES _EXTRA_OPTIONS _EXTRA_INCLUDE nbl_project_process_test_module() endmacro() -# TODO this macro needs more love macro(nbl_create_ext_library_project EXT_NAME LIB_HEADERS LIB_SOURCES LIB_INCLUDES LIB_OPTIONS DEF_OPTIONS) set(LIB_NAME "NblExt${EXT_NAME}") project(${LIB_NAME}) add_library(${LIB_NAME} ${LIB_SOURCES}) - - target_include_directories(${LIB_NAME} - PUBLIC $ - PRIVATE ${LIB_INCLUDES} - ) - - if(NBL_EMBED_BUILTIN_RESOURCES) - get_target_property(_BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY_ nblBuiltinResourceData BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY) - - target_include_directories(${LIB_NAME} - PUBLIC ${_BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY_} - ) - endif() - add_dependencies(${LIB_NAME} Nabla) target_link_libraries(${LIB_NAME} PUBLIC Nabla) - target_compile_options(${LIB_NAME} PUBLIC ${LIB_OPTIONS}) - target_compile_definitions(${LIB_NAME} PUBLIC ${DEF_OPTIONS}) - - nbl_handle_dll_definitions(${LIB_NAME} PUBLIC) - nbl_handle_runtime_lib_properties(${LIB_NAME}) - - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - add_compile_options( - "$<$:-fstack-protector-all>" - ) - - set(COMMON_LINKER_OPTIONS "-msse4.2 -mfpmath=sse -fuse-ld=gold") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${COMMON_LINKER_OPTIONS}") - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${COMMON_LINKER_OPTIONS} -fstack-protector-strong -fsanitize=address") - endif() + target_include_directories(${LIB_NAME} PRIVATE ${LIB_INCLUDES}) nbl_adjust_flags(TARGET ${LIB_NAME} MAP_RELEASE Release MAP_RELWITHDEBINFO RelWithDebInfo MAP_DEBUG Debug) - nbl_adjust_definitions() # macro defined in root CMakeLists + nbl_adjust_definitions() - set_target_properties(${LIB_NAME} PROPERTIES DEBUG_POSTFIX "") - set_target_properties(${LIB_NAME} PROPERTIES RELWITHDEBINFO_POSTFIX _rwdb) - set_target_properties(${LIB_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" + target_compile_options(${LIB_NAME} PUBLIC ${LIB_OPTIONS}) + target_compile_definitions(${LIB_NAME} PUBLIC ${DEF_OPTIONS}) + set_target_properties(${LIB_NAME} PROPERTIES + DEBUG_POSTFIX _d + RELWITHDEBINFO_POSTFIX _rwdi ) - if(MSVC) - set_target_properties(${LIB_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_SOURCE_DIR}/bin" - VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" # seems like has no effect - ) - endif() if(LIB_HEADERS) nbl_install_file_spec(${LIB_HEADERS} "nbl/ext/${EXT_NAME}") - endif() + endif() nbl_install_lib_spec(${LIB_NAME} "nbl/ext/${EXT_NAME}") @@ -1334,4 +1215,17 @@ macro(NBL_DOCKER) RESULT_VARIABLE DOCKER_EXIT_CODE OUTPUT_VARIABLE DOCKER_OUTPUT_VAR ) -endmacro() \ No newline at end of file +endmacro() + +function(NBL_ADJUST_FOLDERS NS) + NBL_GET_ALL_TARGETS(TARGETS) + foreach(T IN LISTS TARGETS) + get_target_property(NBL_FOLDER ${T} FOLDER) + + if(NBL_FOLDER) + set_target_properties(${T} PROPERTIES FOLDER "nabla/${NS}/${NBL_FOLDER}") + else() + set_target_properties(${T} PROPERTIES FOLDER "nabla/${NS}") + endif() + endforeach() +endfunction() \ No newline at end of file diff --git a/examples_tests b/examples_tests index 643b8d8c40..0cbb83ec50 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 643b8d8c400c7f9638dba99937af2f0d428c8849 +Subproject commit 0cbb83ec5034032e213b768ea56eacb3db57472c diff --git a/include/nbl/application_templates/BasicMultiQueueApplication.hpp b/include/nbl/application_templates/BasicMultiQueueApplication.hpp index b4d9f1b843..f39e35ed07 100644 --- a/include/nbl/application_templates/BasicMultiQueueApplication.hpp +++ b/include/nbl/application_templates/BasicMultiQueueApplication.hpp @@ -1,11 +1,13 @@ // Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_EXAMPLES_APPLICATION_TEMPLATES_BASIC_MULTI_QUEUE_APPLICATION_HPP_INCLUDED_ -#define _NBL_EXAMPLES_APPLICATION_TEMPLATES_BASIC_MULTI_QUEUE_APPLICATION_HPP_INCLUDED_ +#ifndef _NBL_APPLICATION_TEMPLATES_BASIC_MULTI_QUEUE_APPLICATION_HPP_INCLUDED_ +#define _NBL_APPLICATION_TEMPLATES_BASIC_MULTI_QUEUE_APPLICATION_HPP_INCLUDED_ + // Build on top of the previous one -#include "MonoDeviceApplication.hpp" +#include "nbl/application_templates/MonoDeviceApplication.hpp" + namespace nbl::application_templates { @@ -263,5 +265,4 @@ class BasicMultiQueueApplication : public virtual MonoDeviceApplication }; } - -#endif // _CAMERA_IMPL_ \ No newline at end of file +#endif \ No newline at end of file diff --git a/include/nbl/application_templates/MonoAssetManagerAndBuiltinResourceApplication.hpp b/include/nbl/application_templates/MonoAssetManagerAndBuiltinResourceApplication.hpp deleted file mode 100644 index 1d3b81098c..0000000000 --- a/include/nbl/application_templates/MonoAssetManagerAndBuiltinResourceApplication.hpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_ASSET_MANAGER_AND_BUILTIN_RESOURCE_APPLICATION_HPP_INCLUDED_ -#define _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_ASSET_MANAGER_AND_BUILTIN_RESOURCE_APPLICATION_HPP_INCLUDED_ - -// we need a system and a logger -#include "MonoSystemMonoLoggerApplication.hpp" -#ifdef NBL_EMBED_BUILTIN_RESOURCES -#include "nbl/this_example/builtin/CArchive.h" -#endif - -namespace nbl::application_templates -{ - -// Virtual Inheritance because apps might end up doing diamond inheritance -class MonoAssetManagerAndBuiltinResourceApplication : public virtual MonoSystemMonoLoggerApplication -{ - using base_t = MonoSystemMonoLoggerApplication; - - public: - using base_t::base_t; - - protected: - // need this one for skipping passing all args into ApplicationFramework - MonoAssetManagerAndBuiltinResourceApplication() = default; - - virtual bool onAppInitialized(core::smart_refctd_ptr&& system) override - { - if (!base_t::onAppInitialized(std::move(system))) - return false; - - using namespace core; - m_assetMgr = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); - - auto resourceArchive = - #ifdef NBL_EMBED_BUILTIN_RESOURCES - make_smart_refctd_ptr(smart_refctd_ptr(m_logger)); - #else - make_smart_refctd_ptr(localInputCWD/"app_resources",smart_refctd_ptr(m_logger),m_system.get()); - #endif - m_system->mount(std::move(resourceArchive),"app_resources"); - - return true; - } - - core::smart_refctd_ptr m_assetMgr; -}; - -} - -#endif // _CAMERA_IMPL_ \ No newline at end of file diff --git a/include/nbl/application_templates/MonoAssetManagerApplication.hpp b/include/nbl/application_templates/MonoAssetManagerApplication.hpp new file mode 100644 index 0000000000..975bc8db47 --- /dev/null +++ b/include/nbl/application_templates/MonoAssetManagerApplication.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_APPLICATION_TEMPLATES_MONO_ASSET_MANAGER_APPLICATION_HPP_INCLUDED_ +#define _NBL_APPLICATION_TEMPLATES_MONO_ASSET_MANAGER_APPLICATION_HPP_INCLUDED_ + + +// we need a system and a logger +#include "nbl/application_templates/MonoSystemMonoLoggerApplication.hpp" + + +namespace nbl::application_templates +{ + +// Virtual Inheritance because apps might end up doing diamond inheritance +class MonoAssetManagerApplication : public virtual MonoSystemMonoLoggerApplication +{ + using base_t = MonoSystemMonoLoggerApplication; + + public: + using base_t::base_t; + + protected: + // need this one for skipping passing all args into ApplicationFramework + MonoAssetManagerApplication() = default; + + virtual bool onAppInitialized(core::smart_refctd_ptr&& system) override + { + if (!base_t::onAppInitialized(std::move(system))) + return false; + + using namespace core; + m_assetMgr = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); + + return true; + } + + core::smart_refctd_ptr m_assetMgr; +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/application_templates/MonoDeviceApplication.hpp b/include/nbl/application_templates/MonoDeviceApplication.hpp index 8006ff36a5..a3a169d7b7 100644 --- a/include/nbl/application_templates/MonoDeviceApplication.hpp +++ b/include/nbl/application_templates/MonoDeviceApplication.hpp @@ -1,11 +1,13 @@ // Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_DEVICE_APPLICATION_HPP_INCLUDED_ -#define _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_DEVICE_APPLICATION_HPP_INCLUDED_ +#ifndef _NBL_APPLICATION_TEMPLATES_MONO_DEVICE_APPLICATION_HPP_INCLUDED_ +#define _NBL_APPLICATION_TEMPLATES_MONO_DEVICE_APPLICATION_HPP_INCLUDED_ + // Build on top of the previous one -#include "MonoSystemMonoLoggerApplication.hpp" +#include "nbl/application_templates/MonoSystemMonoLoggerApplication.hpp" + namespace nbl::application_templates { @@ -41,7 +43,8 @@ class MonoDeviceApplication : public virtual MonoSystemMonoLoggerApplication using namespace nbl::core; using namespace nbl::video; // TODO: specify version of the app - m_api = CVulkanConnection::create(smart_refctd_ptr(m_system),0,_NBL_APP_NAME_,smart_refctd_ptr(base_t::m_logger),getAPIFeaturesToEnable()); + // TODO: take APP NAME from executable metadata, DO NOT use defines in order to allow this to be part of examples PCH + m_api = CVulkanConnection::create(smart_refctd_ptr(m_system),0,"Nabla Example", smart_refctd_ptr(base_t::m_logger), getAPIFeaturesToEnable()); if (!m_api) return logFail("Failed to crate an IAPIConnection!"); @@ -276,5 +279,4 @@ class MonoDeviceApplication : public virtual MonoSystemMonoLoggerApplication }; } - -#endif // _CAMERA_IMPL_ \ No newline at end of file +#endif \ No newline at end of file diff --git a/include/nbl/application_templates/MonoSystemMonoLoggerApplication.hpp b/include/nbl/application_templates/MonoSystemMonoLoggerApplication.hpp index acc3f7a3ed..e995381a40 100644 --- a/include/nbl/application_templates/MonoSystemMonoLoggerApplication.hpp +++ b/include/nbl/application_templates/MonoSystemMonoLoggerApplication.hpp @@ -1,8 +1,8 @@ // Copyright (C) 2023-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_SYSTEM_MONO_LOGGER_APPLICATION_HPP_INCLUDED_ -#define _NBL_EXAMPLES_APPLICATION_TEMPLATES_MONO_SYSTEM_MONO_LOGGER_APPLICATION_HPP_INCLUDED_ +#ifndef _NBL_APPLICATION_TEMPLATES_MONO_SYSTEM_MONO_LOGGER_APPLICATION_HPP_INCLUDED_ +#define _NBL_APPLICATION_TEMPLATES_MONO_SYSTEM_MONO_LOGGER_APPLICATION_HPP_INCLUDED_ // always include nabla first #include "nabla.h" diff --git a/include/nbl/asset/ECommonEnums.h b/include/nbl/asset/ECommonEnums.h index 7db562cc6a..c07a0ced6a 100644 --- a/include/nbl/asset/ECommonEnums.h +++ b/include/nbl/asset/ECommonEnums.h @@ -185,6 +185,123 @@ struct SMemoryBarrier } }; +inline core::bitflag allPreviousStages(core::bitflag stages) +{ + struct PerStagePreviousStages + { + public: + constexpr PerStagePreviousStages() + { + // set all stage to have itself as their previous stages + for (auto i = 0; i < std::numeric_limits::digits; i++) + data[i] = static_cast(i); + + add(PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT, PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT); + + add(PIPELINE_STAGE_FLAGS::RAY_TRACING_SHADER_BIT, PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT); + + // graphics primitive pipeline + PIPELINE_STAGE_FLAGS primitivePrevStage = PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT; + for (auto pipelineStage : {PIPELINE_STAGE_FLAGS::INDEX_INPUT_BIT, PIPELINE_STAGE_FLAGS::VERTEX_ATTRIBUTE_INPUT_BIT, PIPELINE_STAGE_FLAGS::VERTEX_SHADER_BIT, PIPELINE_STAGE_FLAGS::TESSELLATION_CONTROL_SHADER_BIT, PIPELINE_STAGE_FLAGS::TESSELLATION_EVALUATION_SHADER_BIT, PIPELINE_STAGE_FLAGS::GEOMETRY_SHADER_BIT, PIPELINE_STAGE_FLAGS::SHADING_RATE_ATTACHMENT_BIT, PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT, PIPELINE_STAGE_FLAGS::FRAGMENT_SHADER_BIT, PIPELINE_STAGE_FLAGS::LATE_FRAGMENT_TESTS_BIT, PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT}) + { + if (pipelineStage == PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT) + primitivePrevStage |= PIPELINE_STAGE_FLAGS::FRAGMENT_DENSITY_PROCESS_BIT; + add(pipelineStage, primitivePrevStage); + primitivePrevStage |= pipelineStage; + } + + + } + constexpr const auto& operator[](const size_t ix) const {return data[ix];} + + private: + constexpr static uint8_t findLSB(size_t val) + { + for (size_t ix=0ull; ix(stageFlag)); + data[bitIx] |= previousStageFlags; + } + + PIPELINE_STAGE_FLAGS data[std::numeric_limits>::digits] = {}; + }; + + constexpr PerStagePreviousStages bitToAccess = {}; + + core::bitflag retval = PIPELINE_STAGE_FLAGS::NONE; + while (bool(stages.value)) + { + const auto bitIx = hlsl::findLSB(stages); + retval |= bitToAccess[bitIx]; + stages ^= static_cast(0x1u< allLaterStages(core::bitflag stages) +{ + struct PerStageLaterStages + { + public: + constexpr PerStageLaterStages() + { + // set all stage to have itself as their next stages + for (auto i = 0; i < std::numeric_limits::digits; i++) + data[i] = static_cast(i); + + add(PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT, PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT); + add(PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT, PIPELINE_STAGE_FLAGS::RAY_TRACING_SHADER_BIT); + + // graphics primitive pipeline + PIPELINE_STAGE_FLAGS laterStage = PIPELINE_STAGE_FLAGS::NONE; + const auto graphicsPrimitivePipelineOrders = std::array{ PIPELINE_STAGE_FLAGS::DISPATCH_INDIRECT_COMMAND_BIT, PIPELINE_STAGE_FLAGS::INDEX_INPUT_BIT, PIPELINE_STAGE_FLAGS::VERTEX_ATTRIBUTE_INPUT_BIT, PIPELINE_STAGE_FLAGS::VERTEX_SHADER_BIT, PIPELINE_STAGE_FLAGS::TESSELLATION_CONTROL_SHADER_BIT, PIPELINE_STAGE_FLAGS::TESSELLATION_EVALUATION_SHADER_BIT, PIPELINE_STAGE_FLAGS::GEOMETRY_SHADER_BIT, PIPELINE_STAGE_FLAGS::SHADING_RATE_ATTACHMENT_BIT, PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT, PIPELINE_STAGE_FLAGS::FRAGMENT_SHADER_BIT, PIPELINE_STAGE_FLAGS::LATE_FRAGMENT_TESTS_BIT, PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT }; + for (auto iter = graphicsPrimitivePipelineOrders.rbegin(); iter < graphicsPrimitivePipelineOrders.rend(); iter++) + { + const auto pipelineStage = *iter; + add(pipelineStage, laterStage); + laterStage |= pipelineStage; + } + + add(PIPELINE_STAGE_FLAGS::FRAGMENT_DENSITY_PROCESS_BIT, PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT); + } + constexpr const auto& operator[](const size_t ix) const {return data[ix];} + + private: + constexpr static uint8_t findLSB(size_t val) + { + for (size_t ix=0ull; ix(stageFlag)); + data[bitIx] |= laterStageFlags; + } + + PIPELINE_STAGE_FLAGS data[std::numeric_limits>::digits] = {}; + }; + + constexpr PerStageLaterStages bitToAccess = {}; + + core::bitflag retval = PIPELINE_STAGE_FLAGS::NONE; + while (bool(stages.value)) + { + const auto bitIx = hlsl::findLSB(stages); + retval |= bitToAccess[bitIx]; + stages ^= static_cast(0x1u< allAccessesFromStages(core::bitflag stages) { struct PerStageAccesses @@ -257,6 +374,9 @@ inline core::bitflag allAccessesFromStages(core::bitflag retval = ACCESS_FLAGS::NONE; while (bool(stages.value)) { diff --git a/include/nbl/asset/IAsset.h b/include/nbl/asset/IAsset.h index aae73fac2a..a691fa6af6 100644 --- a/include/nbl/asset/IAsset.h +++ b/include/nbl/asset/IAsset.h @@ -83,14 +83,14 @@ class IAsset : virtual public core::IReferenceCounted ET_ANIMATION_LIBRARY = 1ull<<8, //!< asset::ICPUAnimationLibrary ET_PIPELINE_LAYOUT = 1ull<<9, //!< asset::ICPUPipelineLayout ET_SHADER = 1ull<<10, //!< asset::IShader - ET_RENDERPASS_INDEPENDENT_PIPELINE = 1ull<<12, //!< asset::ICPURenderpassIndependentPipeline + ET_GEOMETRY = 1ull<<12, //!< anything inheriting from asset::IGeometry ET_RENDERPASS = 1ull<<13, //!< asset::ICPURenderpass ET_FRAMEBUFFER = 1ull<<14, //!< asset::ICPUFramebuffer ET_GRAPHICS_PIPELINE = 1ull<<15, //!< asset::ICPUGraphicsPipeline ET_BOTOM_LEVEL_ACCELERATION_STRUCTURE = 1ull<<16, //!< asset::ICPUBottomLevelAccelerationStructure ET_TOP_LEVEL_ACCELERATION_STRUCTURE = 1ull<<17, //!< asset::ICPUTopLevelAccelerationStructure - ET_SUB_MESH = 1ull<<18, //!< DEPRECATED asset::ICPUMeshBuffer - ET_MESH = 1ull<<19, //!< DEPRECATED asset::ICPUMesh + ET_GEOMETRY_COLLECTION = 1ull<<18, //!< asset::ICPUGeometryCollection + ET_MORPH_TARGETS = 1ull<<19, //!< asset::ICPUMorphTargets ET_COMPUTE_PIPELINE = 1ull<<20, //!< asset::ICPUComputePipeline ET_PIPELINE_CACHE = 1ull<<21, //!< asset::ICPUPipelineCache ET_SCENE = 1ull<<22, //!< reserved, to implement later diff --git a/include/nbl/asset/IAssetManager.h b/include/nbl/asset/IAssetManager.h index 971c04d818..2105b6c4fe 100644 --- a/include/nbl/asset/IAssetManager.h +++ b/include/nbl/asset/IAssetManager.h @@ -1,9 +1,8 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_ASSET_MANAGER_H_INCLUDED__ -#define __NBL_ASSET_I_ASSET_MANAGER_H_INCLUDED__ +#ifndef _NBL_ASSET_I_ASSET_MANAGER_H_INCLUDED_ +#define _NBL_ASSET_I_ASSET_MANAGER_H_INCLUDED_ #include #include @@ -18,7 +17,7 @@ #include "nbl/asset/interchange/IAssetWriter.h" #include "nbl/asset/utils/CCompilerSet.h" -#include "nbl/asset/utils/IGeometryCreator.h" +#include "nbl/asset/utils/CGeometryCreator.h" #define USE_MAPS_FOR_PATH_BASED_CACHE //benchmark and choose, paths can be full system paths @@ -114,8 +113,6 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted friend class IAssetLoader; friend class IAssetLoader::IAssetLoaderOverride; // for access to non-const findAssets - core::smart_refctd_ptr m_geometryCreator; - core::smart_refctd_ptr m_meshManipulator; core::smart_refctd_ptr m_compilerSet; // called as a part of constructor only void initializeMeshTools(); @@ -139,8 +136,7 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted inline system::ISystem* getSystem() const { return m_system.get(); } - const IGeometryCreator* getGeometryCreator() const; - IMeshManipulator* getMeshManipulator(); + CPolygonGeometryManipulator* getPolygonGeometryManipulator(); CCompilerSet* getCompilerSet() const { return m_compilerSet.get(); } protected: diff --git a/include/nbl/asset/IBuffer.h b/include/nbl/asset/IBuffer.h index fd3ee32a26..8c3b8f95ef 100644 --- a/include/nbl/asset/IBuffer.h +++ b/include/nbl/asset/IBuffer.h @@ -100,6 +100,9 @@ struct SBufferRange inline operator SBufferRange&() {return *reinterpret_cast*>(this);} inline operator const SBufferRange&() const {return *reinterpret_cast*>(this);} + template requires std::is_same_v,BufferType> + inline operator SBufferBinding() const { return {.offset=offset,.buffer=buffer}; } + explicit inline operator bool() const {return isValid();} inline bool isValid() const diff --git a/include/nbl/asset/ICPUBuffer.h b/include/nbl/asset/ICPUBuffer.h index 46105b3c0e..2d10c2907b 100644 --- a/include/nbl/asset/ICPUBuffer.h +++ b/include/nbl/asset/ICPUBuffer.h @@ -25,6 +25,7 @@ namespace nbl::asset class ICPUBuffer final : public asset::IBuffer, public IPreHashed { public: + // TODO: template to make `data` a `const void*` vs `void*` struct SCreationParams : asset::IBuffer::SCreationParams { void* data = nullptr; diff --git a/include/nbl/asset/ICPUGeometryCollection.h b/include/nbl/asset/ICPUGeometryCollection.h new file mode 100644 index 0000000000..df135de117 --- /dev/null +++ b/include/nbl/asset/ICPUGeometryCollection.h @@ -0,0 +1,114 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_CPU_GEOMETRY_COLLECTION_H_INCLUDED_ +#define _NBL_ASSET_I_CPU_GEOMETRY_COLLECTION_H_INCLUDED_ + + +#include "nbl/asset/IAsset.h" +#include "nbl/asset/ICPUBuffer.h" +#include "nbl/asset/IGeometryCollection.h" + + +namespace nbl::asset +{ +// +class NBL_API2 ICPUGeometryCollection : public IAsset, public IGeometryCollection +{ + using base_t = IGeometryCollection; + + public: + inline ICPUGeometryCollection() = default; + + constexpr static inline auto AssetType = ET_GEOMETRY_COLLECTION; + inline E_TYPE getAssetType() const override {return AssetType;} + + // + inline bool valid() const //override + { + for (const auto& ref : m_geometries) + if (!ref.geometry->valid()) + return false; + return true; + } + + inline core::smart_refctd_ptr clone(uint32_t _depth=~0u) const + { + const auto nextDepth = _depth ? (_depth-1):0; + auto retval = core::smart_refctd_ptr(); + retval->m_aabb = m_aabb; + retval->m_inverseBindPoseView = m_inverseBindPoseView.clone(nextDepth); + retval->m_jointAABBView = m_jointAABBView.clone(nextDepth); + retval->m_geometries.reserve(m_geometries.size()); + for (const auto& in : m_geometries) + { + auto& out = retval->m_geometries.emplace_back(); + out.transform = in.transform; + out.geometry = core::smart_refctd_ptr_static_cast>(in.geometry->clone(nextDepth)); + out.jointRedirectView = in.jointRedirectView.clone(nextDepth); + } + return retval; + } + + // + inline bool setAABB(const IGeometryBase::SAABBStorage& aabb) + { + if (isMutable()) + { + m_aabb = aabb; + return true; + } + return false; + } + + // + inline core::vector* getGeometries() + { + if (isMutable()) + return &m_geometries; + return nullptr; + } + + // + inline bool setSkin(SDataView&& inverseBindPoseView, SDataView&& jointAABBView) + { + if (isMutable()) + return setSkin(std::move(inverseBindPoseView),std::move(jointAABBView)); + return false; + } + + // + template// requires std::is_same_v()),decltype(ICPUBottomLevelAccelerationStructure::Triangles&)> + inline Iterator exportForBLAS(Iterator out, uint32_t* pWrittenOrdinals=nullptr) const + { + return exportForBLAS(std::forward(out),[](const hlsl::float32_t3x4& lhs, const hlsl::float32_t3x4& rhs)->void + { + lhs = rhs; + if (pWrittenOrdinals) + *(pWrittenOrdinals++) = (ptrdiff_t(&rhs)-offsetof(SGeometryReference,transform)-ptrdiff_t(base_t::m_geometries.data()))/sizeof(SGeometryReference); + } + ); + } + + protected: + // + inline void visitDependents_impl(std::function visit) const override + { + auto nonNullOnly = [&visit](const IAsset* dep)->bool + { + if (dep) + return visit(dep); + return true; + }; + if (!nonNullOnly(m_inverseBindPoseView.src.buffer.get())) return; + if (!nonNullOnly(m_jointAABBView.src.buffer.get())) return; + for (const auto& ref : m_geometries) + { + const auto* geometry = static_cast(ref.geometry.get()); + if (!nonNullOnly(geometry)) return; + } + } +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/ICPUMesh.h b/include/nbl/asset/ICPUMesh.h deleted file mode 100644 index df647b14a4..0000000000 --- a/include/nbl/asset/ICPUMesh.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_CPU_MESH_H_INCLUDED__ -#define __NBL_ASSET_I_CPU_MESH_H_INCLUDED__ - -#include "nbl/asset/IMesh.h" -#include "nbl/asset/IAsset.h" -#include "nbl/asset/ICPUMeshBuffer.h" - -namespace nbl -{ -namespace asset -{ - -class ICPUMesh final : public IMesh, public IAsset -{ - public: - //! These are not absolute constants, just the most common situation, there may be setups of assets/resources with completely different relationships. - _NBL_STATIC_INLINE_CONSTEXPR uint32_t MESHBUFFER_HIERARCHYLEVELS_BELOW = 1u;//mesh->meshbuffer - _NBL_STATIC_INLINE_CONSTEXPR uint32_t PIPELINE_HIERARCHYLEVELS_BELOW = MESHBUFFER_HIERARCHYLEVELS_BELOW+1u;//meshbuffer->pipeline - _NBL_STATIC_INLINE_CONSTEXPR uint32_t PIPELINE_LAYOUT_HIERARCHYLEVELS_BELOW = PIPELINE_HIERARCHYLEVELS_BELOW +1u;//meshbuffer->pipeline->layout - _NBL_STATIC_INLINE_CONSTEXPR uint32_t DESC_SET_HIERARCHYLEVELS_BELOW = MESHBUFFER_HIERARCHYLEVELS_BELOW+1u;//meshbuffer->ds - _NBL_STATIC_INLINE_CONSTEXPR uint32_t IMAGEVIEW_HIERARCHYLEVELS_BELOW = DESC_SET_HIERARCHYLEVELS_BELOW+1u;//ds->imageview - _NBL_STATIC_INLINE_CONSTEXPR uint32_t IMAGE_HIERARCHYLEVELS_BELOW = IMAGEVIEW_HIERARCHYLEVELS_BELOW+1u;//imageview->image - _NBL_STATIC_INLINE_CONSTEXPR uint32_t SAMPLER_HIERARCHYLEVELS_BELOW = DESC_SET_HIERARCHYLEVELS_BELOW+2u;//ds->layout->immutable - _NBL_STATIC_INLINE_CONSTEXPR uint32_t BUFFER_HIERARCHYLEVELS_BELOW = DESC_SET_HIERARCHYLEVELS_BELOW+1u;//ds->buffer - _NBL_STATIC_INLINE_CONSTEXPR uint32_t VTX_IDX_BUFFER_HIERARCHYLEVELS_BELOW = MESHBUFFER_HIERARCHYLEVELS_BELOW+1u;//meshbuffer->m_vertexBufferBindings or m_indexBufferBinding - - //! - inline core::SRange getMeshBuffers() const override - { - auto begin = reinterpret_cast(m_meshBuffers.data()); - return core::SRange(begin,begin+m_meshBuffers.size()); - } - inline core::SRange getMeshBuffers() override - { - assert(isMutable()); - auto begin = reinterpret_cast(m_meshBuffers.data()); - return core::SRange(begin,begin+m_meshBuffers.size()); - } - - //! Mutable access to the vector of meshbuffers - inline auto& getMeshBufferVector() - { - assert(isMutable()); - return m_meshBuffers; - } - - //! - inline const core::aabbox3df& getBoundingBox() const // needed so the compiler doesn't freak out - { - return IMesh::getBoundingBox(); - } - inline void setBoundingBox(const core::aabbox3df& newBoundingBox) override - { - assert(isMutable()); - return IMesh::setBoundingBox(newBoundingBox); - } - - - _NBL_STATIC_INLINE_CONSTEXPR auto AssetType = ET_MESH; - inline E_TYPE getAssetType() const override { return AssetType; } - - core::smart_refctd_ptr clone(uint32_t _depth = ~0u) const override - { - auto cp = core::make_smart_refctd_ptr(); - cp->m_cachedBoundingBox = m_cachedBoundingBox; - cp->m_meshBuffers.resize(m_meshBuffers.size()); - - auto outIt = cp->m_meshBuffers.begin(); - for (auto inIt=m_meshBuffers.begin(); inIt!=m_meshBuffers.end(); inIt++,outIt++) - { - if (_depth>0u && *inIt) - *outIt = core::smart_refctd_ptr_static_cast((*inIt)->clone(_depth-1u)); - else - *outIt = *inIt; - } - - return cp; - } - - inline bool valid() const override - { - for (const auto& meshBuffer : m_meshBuffers) - { - if (!meshBuffer) return false; - if (!meshBuffer->valid()) return false; - } - return true; - } - - protected: - - private: - core::vector> m_meshBuffers; - - inline void visitDependents_impl(std::function visit) const override - { - } -}; - -} -} - -#endif diff --git a/include/nbl/asset/ICPUMeshBuffer.h b/include/nbl/asset/ICPUMeshBuffer.h deleted file mode 100644 index aa6cbc9429..0000000000 --- a/include/nbl/asset/ICPUMeshBuffer.h +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef _NBL_ASSET_I_CPU_MESH_BUFFER_H_INCLUDED_ -#define _NBL_ASSET_I_CPU_MESH_BUFFER_H_INCLUDED_ - -#include "nbl/asset/IMeshBuffer.h" -#include "nbl/asset/ICPUDescriptorSet.h" -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" -#include "nbl/asset/format/decodePixels.h" -#include "nbl/asset/format/encodePixels.h" - -namespace nbl::asset -{ - -// TODO: This should probably go somewhere else, DEFINITELY SHOULD GO SOMEWHERE ELSE @Crisspl -namespace impl -{ - inline E_FORMAT getCorrespondingIntegerFmt(E_FORMAT _scaledFmt) - { - switch (_scaledFmt) - { - case EF_R8_USCALED: return EF_R8_UINT; - case EF_R8_SSCALED: return EF_R8_SINT; - case EF_R8G8_USCALED: return EF_R8G8_UINT; - case EF_R8G8_SSCALED: return EF_R8G8_SINT; - case EF_R8G8B8_USCALED: return EF_R8G8B8_UINT; - case EF_R8G8B8_SSCALED: return EF_R8G8B8_SINT; - case EF_B8G8R8_USCALED: return EF_B8G8R8_UINT; - case EF_B8G8R8_SSCALED: return EF_B8G8R8_SINT; - case EF_R8G8B8A8_USCALED: return EF_R8G8B8A8_UINT; - case EF_R8G8B8A8_SSCALED: return EF_R8G8B8A8_SINT; - case EF_B8G8R8A8_USCALED: return EF_B8G8R8A8_UINT; - case EF_B8G8R8A8_SSCALED: return EF_B8G8R8A8_SINT; - case EF_A8B8G8R8_USCALED_PACK32: return EF_A8B8G8R8_UINT_PACK32; - case EF_A8B8G8R8_SSCALED_PACK32: return EF_A8B8G8R8_SINT_PACK32; - case EF_A2R10G10B10_USCALED_PACK32: return EF_A2R10G10B10_UINT_PACK32; - case EF_A2R10G10B10_SSCALED_PACK32: return EF_A2R10G10B10_SINT_PACK32; - case EF_A2B10G10R10_USCALED_PACK32: return EF_A2B10G10R10_UINT_PACK32; - case EF_A2B10G10R10_SSCALED_PACK32: return EF_A2B10G10R10_SINT_PACK32; - case EF_R16_USCALED: return EF_R16_UINT; - case EF_R16_SSCALED: return EF_R16_SINT; - case EF_R16G16_USCALED: return EF_R16G16_UINT; - case EF_R16G16_SSCALED: return EF_R16G16_SINT; - case EF_R16G16B16_USCALED: return EF_R16G16B16_UINT; - case EF_R16G16B16_SSCALED: return EF_R16G16B16_SINT; - case EF_R16G16B16A16_USCALED: return EF_R16G16B16A16_UINT; - case EF_R16G16B16A16_SSCALED: return EF_R16G16B16A16_SINT; - - default: return EF_UNKNOWN; - } - } -} - -class ICPUMeshBuffer final : public IMeshBuffer, public IAsset -{ - using base_t = IMeshBuffer; - // knowing the position attribute ID is important for AABB computations etc. - uint32_t posAttrId : 5; - uint32_t normalAttrId : 5; - // by having one attribute only, we limit the number of bones per vertex to 4 - uint32_t jointIDAttrId : 5; - uint32_t jointWeightAttrId : 5; - - protected: - virtual ~ICPUMeshBuffer() = default; - - public: - //! Default constructor (initializes pipeline, desc set and buffer bindings to nullptr) - ICPUMeshBuffer() : base_t(nullptr, nullptr, nullptr, SBufferBinding{}) - { - posAttrId = 0u; - normalAttrId = MAX_VERTEX_ATTRIB_COUNT; - jointIDAttrId = MAX_VERTEX_ATTRIB_COUNT; - jointWeightAttrId = MAX_VERTEX_ATTRIB_COUNT; - } - template - ICPUMeshBuffer(Args&&... args) : base_t(std::forward(args)...) - { - posAttrId = 0u; - normalAttrId = MAX_VERTEX_ATTRIB_COUNT; - jointIDAttrId = MAX_VERTEX_ATTRIB_COUNT; - jointWeightAttrId = MAX_VERTEX_ATTRIB_COUNT; - } - - core::smart_refctd_ptr clone(uint32_t _depth = ~0u) const override - { - core::unordered_map> buffers; - auto cloneBuf = [&buffers,_depth](ICPUBuffer* buf) -> core::smart_refctd_ptr { - if (!buf) - return nullptr; - if (!_depth) - return core::smart_refctd_ptr(buf); - - auto found = buffers.find(buf); - if (found != buffers.end()) - return found->second; - - auto cp = core::smart_refctd_ptr_static_cast(buf->clone(_depth-1u)); - buffers.insert({ buf, cp }); - return cp; - }; - - auto cp = core::make_smart_refctd_ptr(); - - cp->boundingBox = boundingBox; - - cp->m_indexBufferBinding.offset = m_indexBufferBinding.offset; - cp->m_indexBufferBinding.buffer = cloneBuf(m_indexBufferBinding.buffer.get()); - for (uint32_t i = 0u; i < MAX_ATTR_BUF_BINDING_COUNT; ++i) - { - cp->m_vertexBufferBindings[i].offset = m_vertexBufferBindings[i].offset; - cp->m_vertexBufferBindings[i].buffer = cloneBuf(m_vertexBufferBindings[i].buffer.get()); - } - - cp->m_inverseBindPoseBufferBinding.offset = m_inverseBindPoseBufferBinding.offset; - cp->m_inverseBindPoseBufferBinding.buffer = cloneBuf(m_inverseBindPoseBufferBinding.buffer.get()); - cp->m_jointAABBBufferBinding.offset = m_jointAABBBufferBinding.offset; - cp->m_jointAABBBufferBinding.buffer = cloneBuf(m_jointAABBBufferBinding.buffer.get()); - - cp->m_descriptorSet = (_depth > 0u && m_descriptorSet) ? core::smart_refctd_ptr_static_cast(m_descriptorSet->clone(_depth - 1u)) : m_descriptorSet; - - memcpy(cp->m_pushConstantsData, m_pushConstantsData, sizeof(m_pushConstantsData)); - - cp->m_pipeline = (_depth > 0u && m_pipeline) ? core::smart_refctd_ptr_static_cast(m_pipeline->clone(_depth - 1u)) : m_pipeline; - - cp->indexCount = indexCount; - cp->instanceCount = instanceCount; - cp->baseVertex = baseVertex; - cp->baseInstance = baseInstance; - - cp->posAttrId = posAttrId; - cp->normalAttrId = normalAttrId; - cp->jointIDAttrId = jointIDAttrId; - cp->jointWeightAttrId = jointWeightAttrId; - - cp->jointCount = jointCount; - cp->maxJointsPerVx = maxJointsPerVx; - cp->indexType = indexType; - - return cp; - } - - _NBL_STATIC_INLINE_CONSTEXPR auto AssetType = ET_SUB_MESH; - inline E_TYPE getAssetType() const override { return AssetType; } - - inline bool setVertexBufferBinding(SBufferBinding&& bufferBinding, uint32_t bindingIndex) - { - assert(isMutable()); - if(bufferBinding.buffer) - bufferBinding.buffer->addUsageFlags(IBuffer::EUF_VERTEX_BUFFER_BIT); - return base_t::setVertexBufferBinding(std::move(bufferBinding), bindingIndex); - } - - inline void setIndexBufferBinding(SBufferBinding&& bufferBinding) - { - assert(isMutable()); - if(bufferBinding.buffer) - bufferBinding.buffer->addUsageFlags(IBuffer::EUF_INDEX_BUFFER_BIT); - return base_t::setIndexBufferBinding(std::move(bufferBinding)); - } - - //! You need to set skeleton, bind poses and AABBs all at once - inline bool setSkin( - SBufferBinding&& _inverseBindPoseBufferBinding, - SBufferBinding&& _jointAABBBufferBinding, - const uint32_t _jointCount, const uint32_t _maxJointsPerVx - ) override - { - assert(isMutable()); - - if(_inverseBindPoseBufferBinding.buffer) - _inverseBindPoseBufferBinding.buffer->addUsageFlags(IBuffer::EUF_STORAGE_BUFFER_BIT); - if(_jointAABBBufferBinding.buffer) - _jointAABBBufferBinding.buffer->addUsageFlags(IBuffer::EUF_STORAGE_BUFFER_BIT); - - return base_t::setSkin(std::move(_inverseBindPoseBufferBinding),std::move(_jointAABBBufferBinding),_jointCount,_maxJointsPerVx); - } - - //! - inline const ICPUDescriptorSet* getAttachedDescriptorSet() const - { - return base_t::getAttachedDescriptorSet(); - } - inline ICPUDescriptorSet* getAttachedDescriptorSet() - { - //assert(isMutable()); // TODO? @Crisspl? - return m_descriptorSet.get(); - } - inline void setAttachedDescriptorSet(core::smart_refctd_ptr&& descriptorSet) - { - assert(isMutable()); - base_t::setAttachedDescriptorSet(std::move(descriptorSet)); - } - - //! - inline const ICPURenderpassIndependentPipeline* getPipeline() const {return base_t::getPipeline();} - inline ICPURenderpassIndependentPipeline* getPipeline() - { - assert(isMutable()); - return m_pipeline.get(); - } - inline void setPipeline(core::smart_refctd_ptr&& pipeline) override final - { - assert(isMutable()); - base_t::setPipeline(std::move(pipeline)); - } - - // - inline uint32_t getIndexValue(uint32_t _i) const - { - if (!m_indexBufferBinding.buffer) - return _i; - switch (indexType) - { - case EIT_16BIT: - return reinterpret_cast(getIndices())[_i]; - case EIT_32BIT: - return reinterpret_cast(getIndices())[_i]; - default: - break; - } - return _i; - } - - //! Returns id of position attribute. - inline uint32_t getPositionAttributeIx() const { return posAttrId; } - //! Sets id of position atrribute. - inline void setPositionAttributeIx(const uint32_t attrId) - { - assert(isMutable()); - - if (attrId >= MAX_VERTEX_ATTRIB_COUNT) - { - #ifdef _NBL_DEBUG - //os::Printer::log("MeshBuffer setPositionAttributeIx attribute ID out of range!\n",ELL_ERROR); - #endif // _NBL_DEBUG - return; - } - - posAttrId = attrId; - } - - //! Returns id of normal attribute. - inline uint32_t getNormalAttributeIx() const { return normalAttrId; } - - //! Sets id of position atrribute. - inline void setNormalAttributeIx(const uint32_t attrId) - { - assert(isMutable()); - normalAttrId = attrId; - } - - //! Returns id of jointID attribute. - inline uint32_t getJointIDAttributeIx() const { return jointIDAttrId; } - - //! Sets id of joint atrribute. - inline void setJointIDAttributeIx(const uint32_t attrId) - { - assert(isMutable()); - jointIDAttrId = attrId; - } - - //! Returns id of joint weight attribute. - inline uint32_t getJointWeightAttributeIx() const { return jointWeightAttrId; } - - //! Sets id of joint's weight atrribute. - inline void setJointWeightAttributeIx(const uint32_t attrId) - { - assert(isMutable()); - jointWeightAttrId = attrId; - } - - //! Deduces max joint influences from the formats used for the joint attributes - inline uint32_t deduceMaxJointsPerVertex() const - { - auto safelyGetAttributeFormatChannelCount = [&](const uint32_t attrId) -> uint32_t - { - if (!isAttributeEnabled(attrId)) - return 0u; - return getFormatChannelCount(getAttribFormat(attrId)); - }; - return (core::min)(safelyGetAttributeFormatChannelCount(jointIDAttrId),safelyGetAttributeFormatChannelCount(jointWeightAttrId)+1u); - } - - //! Tells us if the mesh is skinned - inline bool isSkinned() const override - { - if (!base_t::isSkinned()) - return false; - return deduceMaxJointsPerVertex()!=0u; - } - - //! Get access to Indices. - /** \return Pointer to indices array. */ - inline void* getIndices() - { - assert(isMutable()); - - if (!m_indexBufferBinding.buffer) - return nullptr; - - return reinterpret_cast(m_indexBufferBinding.buffer->getPointer()) + m_indexBufferBinding.offset; - } - - //! Get access to Indices. - /** We only keep track of a position attribute, as every vertex needs to have at least a position to be displayed on the screen. - Certain vertices may not have colors, normals, texture coords, etc. but a position is always present. - \return Pointer to index array. */ - inline const void* getIndices() const - { - if (!m_indexBufferBinding.buffer) - return nullptr; - - return reinterpret_cast(m_indexBufferBinding.buffer->getPointer()) + m_indexBufferBinding.offset; - } - - //! Accesses given index of mapped position attribute buffer. - /** @param ix Index number of vertex which is to be returned. - @returns `ix`th vertex of mapped attribute buffer or (0, 0, 0, 1) vector if an error occured (e.g. no such vertex). - @see @ref getAttribute() - */ - virtual core::vectorSIMDf getPosition(size_t ix) const - { - core::vectorSIMDf outPos(0.f, 0.f, 0.f, 1.f); - bool success = getAttribute(outPos, posAttrId, ix); - #ifdef _NBL_DEBUG - if (!success) - { - //os::Printer::log("SOME DEBUG MESSAGE!\n",ELL_ERROR); - } - #endif // _NBL_DEBUG - return outPos; - } - - //! Accesses data of buffer of attribute of given id - /** Basically it will get the start of the array at the same point as OpenGL will get upon a glDraw*. - @param attrId Attribute id. - @returns Pointer to corresponding buffer's data incremented by `baseVertex` and by `bufferOffset` - @see @ref getBaseVertex() setBaseVertex() getAttribute() - */ - virtual uint8_t* getAttribPointer(uint32_t attrId) - { - assert(isMutable()); - - if (!m_pipeline) - return nullptr; - - const auto& cachedParams = m_pipeline->getCachedCreationParams(); - const auto& vtxInputParams = cachedParams.vertexInput; - if (!isAttributeEnabled(attrId)) - return nullptr; - - const uint32_t bindingNum = vtxInputParams.attributes[attrId].binding; - if (!isVertexAttribBufferBindingEnabled(bindingNum)) - return nullptr; - - ICPUBuffer* mappedAttrBuf = m_vertexBufferBindings[bindingNum].buffer.get(); - if (!mappedAttrBuf) - return nullptr; - - int64_t ix = vtxInputParams.bindings[bindingNum].inputRate!=SVertexInputBindingParams::EVIR_PER_VERTEX ? baseInstance:baseVertex; - ix *= vtxInputParams.bindings[bindingNum].stride; - ix += (m_vertexBufferBindings[bindingNum].offset + vtxInputParams.attributes[attrId].relativeOffset); - if (ix < 0 || static_cast(ix) >= mappedAttrBuf->getSize()) - return nullptr; - - return reinterpret_cast(mappedAttrBuf->getPointer()) + ix; - } - inline const uint8_t* getAttribPointer(uint32_t attrId) const - { - return const_cast::type&>(*this).getAttribPointer(attrId); - } - - static inline bool getAttribute(core::vectorSIMDf& output, const void* src, E_FORMAT format) - { - if (!src) - return false; - - bool scaled = false; - if (!isNormalizedFormat(format) && !isFloatingPointFormat(format) && !(scaled = isScaledFormat(format))) - return false; - - if (!scaled) - { - double output64[4]{ 0., 0., 0., 1. }; - decodePixels(format, &src, output64, 0u, 0u); - for (auto i=0u; i<4u; i++) - output[i] = static_cast(output64[i]); - } - else - { - if (isSignedFormat(format)) - { - int64_t output64i[4]{ 0, 0, 0, 1 }; - decodePixels(impl::getCorrespondingIntegerFmt(format), &src, output64i, 0u, 0u); - for (auto i=0u; i<4u; i++) - output[i] = static_cast(output64i[i]); - } - else - { - uint64_t output64u[4]{ 0u, 0u, 0u, 1u }; - decodePixels(impl::getCorrespondingIntegerFmt(format), &src, output64u, 0u, 0u); - for (auto i=0u; i<4u; i++) - output[i] = static_cast(output64u[i]); - } - } - - return true; - } - - //! Accesses vertex of given index of given vertex attribute. Index number is incremented by `baseVertex`. WARNING: NOT ALL FORMAT CONVERSIONS TO RGBA32F/XYZW32F ARE IMPLEMENTED! - /** If component count of given attribute is less than 4, only first ones of output vector's members will be written. - @param[out] output vectorSIMDf object to which index's value will be returned. - @param[in] attrId Atrribute id. - @param[in] ix Index which is to be accessed. Will be incremented by `baseVertex`. - @returns true if successful or false if an error occured (e.g. `ix` out of range, no attribute specified/bound or given attribute's format conversion to vectorSIMDf unsupported). - @see @ref getBaseVertex() setBaseVertex() getAttribute() - */ - virtual bool getAttribute(core::vectorSIMDf& output, uint32_t attrId, size_t ix) const - { - if (!isAttributeEnabled(attrId)) - return false; - - const uint32_t bindingId = getBindingNumForAttribute(attrId); - - const uint8_t* src = getAttribPointer(attrId); - src += ix * getAttribStride(attrId); - if (src >= reinterpret_cast(m_vertexBufferBindings[bindingId].buffer->getPointer()) + m_vertexBufferBindings[bindingId].buffer->getSize()) - return false; - - return getAttribute(output, src, getAttribFormat(attrId)); - } - - static inline bool getAttribute(uint32_t* output, const void* src, E_FORMAT format) - { - if (!src) - return false; - - bool scaled = false; - if ((scaled = isScaledFormat(format)) || isIntegerFormat(format)) - { - if (isSignedFormat(format)) - { - int64_t output64[4]{0, 0, 0, 1}; - decodePixels(scaled ? impl::getCorrespondingIntegerFmt(format) : format, &src, output64, 0u, 0u); - for (uint32_t i = 0u; i < getFormatChannelCount(format); ++i) - output[i] = static_cast(output64[i]); - } - else - { - uint64_t output64[4]{0u, 0u, 0u, 1u}; - decodePixels(scaled ? impl::getCorrespondingIntegerFmt(format) : format, &src, output64, 0u, 0u); - for (uint32_t i = 0u; i < getFormatChannelCount(format); ++i) - output[i] = static_cast(output64[i]); - } - return true; - } - - return false; - } - - //! Accesses vertex of given index of given vertex attribute. Index number is incremented by `baseVertex`. WARNING: NOT ALL FORMAT CONVERSIONS TO RGBA32F/XYZW32F ARE IMPLEMENTED! - /** If component count of given attribute is less than 4, only first ones of output vector's members will be written. - Attributes of integer types smaller than 32 bits are promoted to 32bit integer. - @param[out] output Pointer to memory to which index's value will be returned. - @param[in] attrId Atrribute id. - @param[in] ix Index which is to be accessed. Will be incremented by `baseVertex`. - @returns true if successful or false if an error occured (e.g. `ix` out of range, no attribute specified/bound or given attribute's format conversion to vectorSIMDf unsupported). - @see @ref getBaseVertex() setBaseVertex() getAttribute() - */ - virtual bool getAttribute(uint32_t* output, uint32_t attrId, size_t ix) const - { - if (!m_pipeline) - return false; - if (!isAttributeEnabled(attrId)) - return false; - - const uint8_t* src = getAttribPointer(attrId); - src += ix * getAttribStride(attrId); - const ICPUBuffer* buf = base_t::getAttribBoundBuffer(attrId).buffer.get(); - if (!buf || src >= reinterpret_cast(buf->getPointer()) + buf->getSize()) - return false; - - return getAttribute(output, src, getAttribFormat(attrId)); - } - - static inline bool setAttribute(core::vectorSIMDf input, void* dst, E_FORMAT format) - { - bool scaled = false; - if (!dst || (!isFloatingPointFormat(format) && !isNormalizedFormat(format) && !(scaled = isScaledFormat(format)))) - return false; - - double input64[4]; - for (uint32_t i = 0u; i < 4u; ++i) - input64[i] = input.pointer[i]; - - if (!scaled) - encodePixels(format, dst, input64); - else - { - if (isSignedFormat(format)) - { - int64_t input64i[4]{ static_cast(input64[0]), static_cast(input64[1]), static_cast(input64[2]), static_cast(input64[3]) }; - encodePixels(impl::getCorrespondingIntegerFmt(format), dst, input64i); - } - else - { - uint64_t input64u[4]{ static_cast(input64[0]), static_cast(input64[1]), static_cast(input64[2]), static_cast(input64[3]) }; - encodePixels(impl::getCorrespondingIntegerFmt(format), dst, input64u); - } - } - - return true; - } - - //! Sets value of vertex of given index of given attribute. WARNING: NOT ALL FORMAT CONVERSIONS FROM RGBA32F/XYZW32F (vectorSIMDf) ARE IMPLEMENTED! - /** @param input Value which is to be set. - @param attrId Atrribute id. - @param ix Index of vertex which is to be set. Will be incremented by `baseVertex`. - @returns true if successful or false if an error occured (e.g. no such index). - @see @ref getBaseVertex() setBaseVertex() getAttribute() - */ - virtual bool setAttribute(core::vectorSIMDf input, uint32_t attrId, size_t ix) - { - assert(isMutable()); - if (!m_pipeline) - return false; - if (!isAttributeEnabled(attrId)) - return false; - - uint8_t* dst = getAttribPointer(attrId); - dst += ix * getAttribStride(attrId); - ICPUBuffer* buf = getAttribBoundBuffer(attrId).buffer.get(); - if (!buf || dst >= ((const uint8_t*)(buf->getPointer())) + buf->getSize()) - return false; - - return setAttribute(input, dst, getAttribFormat(attrId)); - } - - static inline bool setAttribute(const uint32_t* _input, void* dst, E_FORMAT format) - { - const bool scaled = isScaledFormat(format); - if (!dst || !(scaled || isIntegerFormat(format))) - return false; - uint8_t* vxPtr = reinterpret_cast(dst); - - if (isSignedFormat(format)) - { - int64_t input[4]; - for (uint32_t i = 0u; i < 4u; ++i) - input[i] = reinterpret_cast(_input)[i]; - encodePixels(scaled ? impl::getCorrespondingIntegerFmt(format) : format, dst, input); - } - else - { - uint64_t input[4]; - for (uint32_t i = 0u; i < 4u; ++i) - input[i] = _input[i]; - encodePixels(scaled ? impl::getCorrespondingIntegerFmt(format) : format, dst, input); - } - return true; - } - - //! @copydoc setAttribute(core::vectorSIMDf, const E_VERTEX_ATTRIBUTE_ID, size_t) - virtual bool setAttribute(const uint32_t* _input, uint32_t attrId, size_t ix) - { - assert(isMutable()); - if (!m_pipeline) - return false; - if (!isAttributeEnabled(attrId)) - return false; - - uint8_t* dst = getAttribPointer(attrId); - dst += ix * getAttribStride(attrId); - ICPUBuffer* buf = getAttribBoundBuffer(attrId).buffer.get(); - if (dst >= ((const uint8_t*)(buf->getPointer())) + buf->getSize()) - return false; - - return setAttribute(_input, dst, getAttribFormat(attrId)); - } - - //! - inline const core::matrix3x4SIMD* getInverseBindPoses() const - { - if (!m_inverseBindPoseBufferBinding.buffer) - return nullptr; - - const uint8_t* ptr = reinterpret_cast(m_inverseBindPoseBufferBinding.buffer->getPointer()); - return reinterpret_cast(ptr+m_inverseBindPoseBufferBinding.offset); - } - inline core::matrix3x4SIMD* getInverseBindPoses() - { - assert(isMutable()); - return const_cast(const_cast(this)->getInverseBindPoses()); - } - - //! - inline const core::aabbox3df* getJointAABBs() const - { - if (!m_jointAABBBufferBinding.buffer) - return nullptr; - - const uint8_t* ptr = reinterpret_cast(m_jointAABBBufferBinding.buffer->getPointer()); - return reinterpret_cast(ptr+ m_jointAABBBufferBinding.offset); - } - inline core::aabbox3df* getJointAABBs() - { - assert(isMutable()); - return const_cast(const_cast(this)->getJointAABBs()); - } - inline bool valid() const override - { - return true; - } - - private: - inline void visitDependents_impl(std::function visit) const override - { - } -}; - -} - -#endif diff --git a/include/nbl/asset/ICPUMorphTargets.h b/include/nbl/asset/ICPUMorphTargets.h new file mode 100644 index 0000000000..545d2cd8a9 --- /dev/null +++ b/include/nbl/asset/ICPUMorphTargets.h @@ -0,0 +1,72 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_CPU_MORPH_TARGETS_H_INCLUDED_ +#define _NBL_ASSET_I_CPU_MORPH_TARGETS_H_INCLUDED_ + + +#include "nbl/asset/IAsset.h" +#include "nbl/asset/IMorphTargets.h" + + +namespace nbl::asset +{ +// +class NBL_API2 ICPUMorphTargets : public IAsset, public IMorphTargets +{ + using base_t = IMorphTargets; + + public: + inline ICPUMorphTargets() = default; + + constexpr static inline auto AssetType = ET_MORPH_TARGETS; + inline E_TYPE getAssetType() const override {return AssetType;} + + // + inline bool valid() const //override + { + for (const auto& target : m_targets) + if (!target || !target.geoCollection->valid()) + return false; + return true; + } + + inline core::smart_refctd_ptr clone(uint32_t _depth=~0u) const + { + const auto nextDepth = _depth ? (_depth-1):0; + auto retval = core::smart_refctd_ptr(); + retval->m_targets.reserve(m_targets.size()); + for (const auto& in : m_targets) + { + auto& out = retval->m_targets.emplace_back(); + out.geoCollection = core::smart_refctd_ptr_static_cast(in.geoCollection->clone(nextDepth)); + out.jointRedirectView = in.jointRedirectView.clone(nextDepth); + } + return retval; + } + + // + inline core::vector* getTargets() + { + if (isMutable()) + return &m_targets; + return nullptr; + } + + protected: + // + inline void visitDependents_impl(std::function visit) const //override + { + auto nonNullOnly = [&visit](const IAsset* dep)->bool + { + if (dep) + return visit(dep); + return true; + }; + for (const auto& ref : m_targets) + if (!nonNullOnly(ref.geoCollection.get())) return; + } +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/ICPUPolygonGeometry.h b/include/nbl/asset/ICPUPolygonGeometry.h new file mode 100644 index 0000000000..b1323de701 --- /dev/null +++ b/include/nbl/asset/ICPUPolygonGeometry.h @@ -0,0 +1,179 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_CPU_POLYGON_GEOMETRY_H_INCLUDED_ +#define _NBL_ASSET_I_CPU_POLYGON_GEOMETRY_H_INCLUDED_ + + +#include "nbl/asset/IAsset.h" +#include "nbl/asset/ICPUBuffer.h" +#include "nbl/asset/IPolygonGeometry.h" + + +namespace nbl::asset +{ +// +class NBL_API2 ICPUPolygonGeometry final : public IPolygonGeometry +{ + using base_t = IPolygonGeometry; + + public: + using SDataView = base_t::SDataView; + + inline ICPUPolygonGeometry() = default; + + constexpr static inline auto AssetType = ET_GEOMETRY; + inline E_TYPE getAssetType() const override {return AssetType;} + + inline bool valid() const override {return base_t::valid();} + + inline core::smart_refctd_ptr clone(uint32_t _depth=~0u) const override + { + const auto nextDepth = _depth ? (_depth-1):0; + auto retval = core::smart_refctd_ptr(); + retval->m_positionView = m_positionView.clone(nextDepth); + retval->m_jointOBBView = m_jointOBBView.clone(nextDepth); + retval->m_indexView = m_indexView.clone(nextDepth); + retval->m_jointWeightViews.reserve(m_jointWeightViews.size()); + for (const auto& pair : m_jointWeightViews) + retval->m_jointWeightViews.push_back({ + .indices = pair.indices.clone(nextDepth), + .weights = pair.weights.clone(nextDepth) + }); + retval->m_auxAttributeViews.reserve(m_auxAttributeViews.size()); + for (const auto& view : m_auxAttributeViews) + retval->m_auxAttributeViews.push_back(view.clone(nextDepth)); + retval->m_normalView = m_normalView.clone(nextDepth); + retval->m_jointCount = m_jointCount; + return retval; + } + + // + inline bool setPositionView(SDataView&& view) + { + if (isMutable() && (!view || view.composed.isFormatted())) + { + m_positionView = std::move(view); + return true; + } + return false; + } + + // + inline bool setJointOBBView(SDataView&& view) + { + if (isMutable()) + return base_t::setJointOBBView(std::move(view)); + return false; + } + + // + inline bool setIndexView(SDataView&& view) + { + if (isMutable()) + return base_t::setIndexView(std::move(view)); + return false; + } + + // + inline bool setIndexing(const IIndexingCallback* indexing) + { + if (isMutable()) + { + m_indexing = indexing; + return true; + } + return false; + } + // + inline bool setNormalView(SDataView&& view) + { + if (isMutable() && (!view || view.composed.getStride()>0)) + { + m_normalView = std::move(view); + return true; + } + return false; + } + + // + inline bool setJointCount(const uint32_t count) + { + if (isMutable()) + { + m_jointCount = count; + return true; + } + return false; + } + + // + inline const core::vector& getJointWeightViews() const {return base_t::getJointWeightViews();} + inline core::vector* getJointWeightViews() + { + if (isMutable()) + return &m_jointWeightViews; + return nullptr; + } + + // TODO: either SoA or AoS but add names + inline const core::vector& getAuxAttributeViews() const {return base_t::getAuxAttributeViews();} + inline core::vector* getAuxAttributeViews() + { + if (isMutable()) + return &m_auxAttributeViews; + return nullptr; + } + + // We don't care about primitive restart, you need to check for it yourself. + // Unlike OpenGL and other APIs we don't adjust the Primitive ID because that breaks parallel processing. + // So a triangle strip `{ 0 1 2 3 RESTART 2 3 4 5 }` means 7 primitives, of which 3 are invalid (contain the restart index) + template requires hlsl::concepts::UnsignedIntegralScalar + inline bool getPrimitiveIndices(Out* out, const uint32_t beginPrimitive, const uint32_t endPrimitive) const + { + if (!m_indexing) + return false; + IIndexingCallback::SContext ctx = { + .indexBuffer = m_indexView.getPointer(), + .indexSize = getTexelOrBlockBytesize(m_indexView.composed.format), + .beginPrimitive = beginPrimitive, + .endPrimitive = endPrimitive, + .out = out + }; + m_indexing->operator()(ctx); + return true; + } + + // + template + inline bool getPrimitiveIndices(Out* out, const uint32_t primitiveID) const + { + return getPrimitiveIndices(out,primitiveID,primitiveID+1); + } + + protected: + // + inline void visitDependents_impl(std::function visit) const override + { + auto nonNullOnly = [&visit](const IAsset* dep)->bool + { + if (dep) + return visit(dep); + return true; + }; + if (!nonNullOnly(m_positionView.src.buffer.get())) return; + if (!nonNullOnly(m_jointOBBView.src.buffer.get())) return; + if (!nonNullOnly(m_indexView.src.buffer.get())) return; + for (const auto& pair : m_jointWeightViews) + { + if (!nonNullOnly(pair.indices.src.buffer.get())) return; + if (!nonNullOnly(pair.weights.src.buffer.get())) return; + } + for (const auto& view : m_auxAttributeViews) + if (!nonNullOnly(view.src.buffer.get())) return; + if (!nonNullOnly(m_normalView.src.buffer.get())) return; + } +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/ICPURenderpassIndependentPipeline.h b/include/nbl/asset/ICPURenderpassIndependentPipeline.h deleted file mode 100644 index 3d67af23d0..0000000000 --- a/include/nbl/asset/ICPURenderpassIndependentPipeline.h +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_ASSET_I_CPU_RENDERPASS_INDEPENDENT_PIPELINE_H_INCLUDED_ -#define _NBL_ASSET_I_CPU_RENDERPASS_INDEPENDENT_PIPELINE_H_INCLUDED_ - -#include "nbl/asset/IRenderpassIndependentPipeline.h" -#include "nbl/asset/ICPUPipelineLayout.h" -#include "nbl/asset/IShader.h" - -namespace nbl::asset -{ - -//! CPU Version of Renderpass Independent Pipeline -/* - @see IRenderpassIndependentPipeline -*/ - -class ICPURenderpassIndependentPipeline : public IRenderpassIndependentPipeline, public IAsset -{ - public: - struct SCreationParams - { - std::span shaders = {}; - SCachedCreationParams cached = {}; - }; - - //(TODO) it is true however it causes DSs to not be cached when ECF_DONT_CACHE_TOP_LEVEL is set which isnt really intuitive - constexpr static inline uint32_t DESC_SET_HIERARCHYLEVELS_BELOW = 0u; - // TODO: @Crisspl HOW ON EARTH DOES THIS MAKE SENSE!? - constexpr static inline uint32_t IMAGEVIEW_HIERARCHYLEVELS_BELOW = 1u; - constexpr static inline uint32_t IMAGE_HIERARCHYLEVELS_BELOW = 2u; - // from here its good - constexpr static inline uint32_t PIPELINE_LAYOUT_HIERARCHYLEVELS_BELOW = 1u; - constexpr static inline uint32_t DESC_SET_LAYOUT_HIERARCHYLEVELS_BELOW = 1u+ICPUPipelineLayout::DESC_SET_LAYOUT_HIERARCHYLEVELS_BELOW; - constexpr static inline uint32_t IMMUTABLE_SAMPLER_HIERARCHYLEVELS_BELOW = 1u+ICPUPipelineLayout::IMMUTABLE_SAMPLER_HIERARCHYLEVELS_BELOW; - constexpr static inline uint32_t SPECIALIZED_SHADER_HIERARCHYLEVELS_BELOW = 1u; - - static core::smart_refctd_ptr create(core::smart_refctd_ptr&& _layout, const SCreationParams& params) - { - // we'll validate the specialization info later when attempting to set it - if (!_layout || params.shaders.empty()) - return nullptr; - auto retval = new ICPURenderpassIndependentPipeline(std::move(_layout),params.cached); -#if 0 - for (const auto spec : params.shaders) - if (spec.shader) - retval->setSpecInfo(spec); -#endif - return core::smart_refctd_ptr(retval,core::dont_grab); - } - - // IAsset implementations - core::smart_refctd_ptr clone(const uint32_t _depth = ~0u) const override - { - core::smart_refctd_ptr layout; - if (_depth>0u && m_layout) - layout = core::smart_refctd_ptr_static_cast(m_layout->clone(_depth-1u)); - else - layout = m_layout; - - auto cp = new ICPURenderpassIndependentPipeline(std::move(layout),m_cachedParams); -#if 0 - for (const auto spec : m_infos) - if (spec.shader) - cp->setSpecInfo(spec); -#endif - - return core::smart_refctd_ptr(cp,core::dont_grab); - } - - _NBL_STATIC_INLINE_CONSTEXPR auto AssetType = ET_RENDERPASS_INDEPENDENT_PIPELINE; - inline E_TYPE getAssetType() const override { return AssetType; } - - // - inline const SCachedCreationParams& getCachedCreationParams() const {return IRenderpassIndependentPipeline::getCachedCreationParams();} - inline SCachedCreationParams& getCachedCreationParams() - { - assert(isMutable()); - return m_cachedParams; - } - - // extras for this class - inline ICPUPipelineLayout* getLayout() - { - assert(isMutable()); - return m_layout.get(); - } - const inline ICPUPipelineLayout* getLayout() const { return m_layout.get(); } - inline void setLayout(core::smart_refctd_ptr&& _layout) - { - assert(isMutable()); - m_layout = std::move(_layout); - } - - inline bool valid() const override - { - return m_layout && m_layout->valid(); - } - -#if 0 - // The getters are weird because the shader pointer needs patching - inline IShader::SSpecInfo getSpecInfos(const hlsl::ShaderStage stage) - { - assert(isMutable()); - const auto stageIx = hlsl::findLSB(stage); - if (stageIx<0 || stageIx>=GRAPHICS_SHADER_STAGE_COUNT || hlsl::bitCount(stage)!=1) - return {}; - return m_infos[stageIx]; - } - inline IShader::SSpecInfo getSpecInfos(const hlsl::ShaderStage stage) const - { - const auto stageIx = hlsl::findLSB(stage); - if (stageIx<0 || stageIx>=GRAPHICS_SHADER_STAGE_COUNT || hlsl::bitCount(stage)!=1) - return {}; - return m_infos[stageIx]; - } - inline bool setSpecInfo(const IShader::SSpecInfo& info) - { - assert(isMutable()); - const int64_t specSize = info.valid(); - if (specSize<0) - return false; - const auto stage = info.shader->getStage(); - const auto stageIx = hlsl::findLSB(stage); - if (stageIx<0 || stageIx>=GRAPHICS_SHADER_STAGE_COUNT || hlsl::bitCount(stage)!=1) - return false; - m_infos[stageIx] = info; - m_shaders[stageIx] = core::smart_refctd_ptr(info.shader); - m_infos[stageIx].shader = m_shaders[stageIx].get(); - if (specSize>0) - { - m_entries[stageIx] = std::make_unique(); - m_entries[stageIx]->reserve(info.entries->size()); - std::copy(info.entries->begin(),info.entries->end(),std::insert_iterator(*m_entries[stageIx],m_entries[stageIx]->begin())); - } - else - m_entries[stageIx] = nullptr; - m_infos[stageIx].entries = m_entries[stageIx].get(); - return true; - } -#endif - - protected: - ICPURenderpassIndependentPipeline(core::smart_refctd_ptr&& _layout, const IRenderpassIndependentPipeline::SCachedCreationParams& params) - : IRenderpassIndependentPipeline(params), m_layout(std::move(_layout)) {} - virtual ~ICPURenderpassIndependentPipeline() = default; - - core::smart_refctd_ptr m_layout; -#if 0 - std::array,GRAPHICS_SHADER_STAGE_COUNT> m_shaders = {}; - std::array,GRAPHICS_SHADER_STAGE_COUNT> m_entries = {}; - std::array m_infos = {}; -#endif - - private: - - inline void visitDependents_impl(std::function visit) const override - { - } -}; - -} -#endif diff --git a/include/nbl/asset/IGeometry.h b/include/nbl/asset/IGeometry.h new file mode 100644 index 0000000000..9c5d0d2350 --- /dev/null +++ b/include/nbl/asset/IGeometry.h @@ -0,0 +1,495 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_GEOMETRY_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_H_INCLUDED_ + + +#include "nbl/builtin/hlsl/shapes/aabb.hlsl" + +#include "nbl/asset/IAsset.h" +#include "nbl/asset/format/EFormat.h" + + +namespace nbl::asset +{ +class IGeometryBase : public virtual core::IReferenceCounted +{ + public: + // used for same purpose as and overlaps `IAsset::valid()` + virtual bool valid() const = 0; + + enum class EPrimitiveType : uint8_t + { + Polygon = 0, + // do not overengineer +// AABBs = 1, + // LSS, Beziers etc. + }; + // + virtual EPrimitiveType getPrimitiveType() const = 0; + + // + enum class EAABBFormat : uint8_t + { + F64, + U64, + S64, + F32, + U32, + S32, + F16, + U16, + U16_NORM, + S16, + S16_NORM, + U8, + U8_NORM, + S8, + S8_NORM, + BitCount=4 + }; + // + static inline EAABBFormat getMatchingAABBFormat(const E_FORMAT attributeFormat) + { + if (isBlockCompressionFormat(attributeFormat)) + return EAABBFormat::BitCount; + if (isFloatingPointFormat(attributeFormat)) + { + const auto maxVal = getFormatMaxValue(attributeFormat,0); + if (maxVal>hlsl::numeric_limits::max) + return EAABBFormat::F64; + if (maxVal>hlsl::numeric_limits::max) + return EAABBFormat::F32; + return EAABBFormat::F16; + } + else if (isNormalizedFormat(attributeFormat)) + { + const auto precision = getFormatPrecision(attributeFormat,0,0.f); + const auto minVal = getFormatMinValue(attributeFormat,0); + if (minVal<-0.f) + return precision(EF_R8_SNORM,0,0.f) ? EAABBFormat::S16_NORM:EAABBFormat::S8_NORM; + else + return precision(EF_R8_UNORM,0,0.f) ? EAABBFormat::U16_NORM:EAABBFormat::U8_NORM; + } + else if (isIntegerFormat(attributeFormat)) + { + if (isSignedFormat(attributeFormat)) + { + const auto maxVal = getFormatMaxValue(attributeFormat,0); + if (maxVal>hlsl::numeric_limits::max) + return EAABBFormat::S64; + else if (maxVal>hlsl::numeric_limits::max) + return EAABBFormat::S32; + else if (maxVal>hlsl::numeric_limits::max) + return EAABBFormat::S16; + return EAABBFormat::S8; + } + else + { + const auto maxVal = getFormatMaxValue(attributeFormat,0); + if (maxVal>hlsl::numeric_limits::max) + return EAABBFormat::U64; + else if (maxVal>hlsl::numeric_limits::max) + return EAABBFormat::U32; + else if (maxVal>hlsl::numeric_limits::max) + return EAABBFormat::U16; + return EAABBFormat::U8; + + } + } + return EAABBFormat::BitCount; + } + // using `nbl::hlsl::` concepts instead of `std::` so that `nbl::hlsl::float16_t` can be used + union SAABBStorage + { + template + inline void visit(const EAABBFormat format, Visitor&& visitor) + { + switch (format) + { + case EAABBFormat::F64: + visitor(f64); + break; + case EAABBFormat::U64: + visitor(u64); + break; + case EAABBFormat::S64: + visitor(s64); + break; + case EAABBFormat::F32: + visitor(f32); + break; + case EAABBFormat::U32: + visitor(u32); + break; + case EAABBFormat::S32: + visitor(s32); + break; + case EAABBFormat::F16: + visitor(f16); + break; + case EAABBFormat::U16: [[fallthrough]]; + case EAABBFormat::U16_NORM: + visitor(u16); + break; + case EAABBFormat::S16: [[fallthrough]]; + case EAABBFormat::S16_NORM: + visitor(s16); + break; + case EAABBFormat::U8: [[fallthrough]]; + case EAABBFormat::U8_NORM: + visitor(u8); + break; + case EAABBFormat::S8: [[fallthrough]]; + case EAABBFormat::S8_NORM: + visitor(s8); + break; + default: + break; + } + } + template + inline void visit(const EAABBFormat format, Visitor&& visitor) const + { + const_cast(this)->visit(format,std::forward(visitor)); + } + + hlsl::shapes::AABB<4,hlsl::float64_t> f64 = hlsl::shapes::AABB<4,hlsl::float64_t>::create(); + hlsl::shapes::AABB<4,uint64_t> u64; + hlsl::shapes::AABB<4,int64_t> s64; + hlsl::shapes::AABB<4,hlsl::float32_t> f32; + hlsl::shapes::AABB<4,uint32_t> u32; + hlsl::shapes::AABB<4,int32_t> s32; + hlsl::shapes::AABB<4,hlsl::float16_t> f16; + hlsl::shapes::AABB<4,uint16_t> u16; + hlsl::shapes::AABB<4,int16_t> s16; + hlsl::shapes::AABB<4,uint8_t> u8; + hlsl::shapes::AABB<4,int8_t> s8; + }; + struct SDataViewBase + { + // mostly checking validity of the format + inline operator bool() const {return format==EF_UNKNOWN || !isBlockCompressionFormat(format) && !isDepthOrStencilFormat(format);} + + // + inline bool isFormatted() const {return format!=EF_UNKNOWN && bool(*this);} + + // Useful for checking if something can be used as an index + inline bool isFormattedScalarInteger() const + { + if (isFormatted()) + switch (format) + { + case EF_R8_SINT: [[fallthrough]]; + case EF_R8_UINT: [[fallthrough]]; + case EF_R16_SINT: [[fallthrough]]; + case EF_R16_UINT: [[fallthrough]]; + case EF_R32_SINT: [[fallthrough]]; + case EF_R32_UINT: [[fallthrough]]; + case EF_R64_SINT: [[fallthrough]]; + case EF_R64_UINT: + return true; + default: + break; + } + return false; + } + + // + inline uint32_t getStride() const + { + if (isFormatted()) + return getFormatClassBlockBytesize(getFormatClass(format)); + return stride; + } + + // + template + inline void visitAABB(Visitor&& visitor) {encodedDataRange.visit(rangeFormat,std::forward(visitor));} + template + inline void visitAABB(Visitor&& visitor) const {encodedDataRange.visit(rangeFormat,std::forward(visitor));} + + // + inline void resetRange(const EAABBFormat newFormat) + { + rangeFormat = newFormat; + auto tmp = [](auto& aabb)->void{aabb = aabb.create();}; + visitAABB(tmp); + } + inline void resetRange() {resetRange(rangeFormat);} + + // + template + inline AABB getRange() const + { + AABB retval = AABB::create(); + auto tmp = [&retval](const auto& aabb)->void + { + retval.minVx = aabb.minVx; + retval.maxVx = aabb.maxVx; + }; + visitAABB(tmp); + return retval; + } + + // optional, really only meant for formatted views + SAABBStorage encodedDataRange = {}; + // 0 means no fixed stride, totally variable data inside + uint32_t stride = 0; + // Format takes precedence over stride + // Note :If format is UNORM or SNORM, the vertex data is relative to the AABB (range) + E_FORMAT format = EF_UNKNOWN; + // tells you which `encodedDataRange` union member to access + EAABBFormat rangeFormat : int(EAABBFormat::BitCount) = EAABBFormat::F64; + }; + + virtual EAABBFormat getAABBFormat() const = 0; + virtual const SAABBStorage& getAABB() const = 0; + template + inline void visitAABB(Visitor&& visitor) const + { + getAABB().visit(getAABBFormat(),std::forward(visitor)); + } + + protected: + virtual inline ~IGeometryBase() = default; +}; + +// a thing to expose `clone()` conditionally via inheritance and `conditional_t` +namespace impl +{ +class NBL_FORCE_EBO NBL_NO_VTABLE INotCloneable {}; +} + +// A geometry should map 1:1 to a BLAS geometry, Meshlet or a Drawcall in API terms +template +class IGeometry : public std::conditional_t,IAsset,impl::INotCloneable>, public IGeometryBase +{ + public: + // + virtual inline bool valid() const override + { + if (!m_positionView) + return false; + if (getPrimitiveCount()==0) + return false; + // joint OBBs are optional + return true; + } + + struct SDataView + { + inline operator bool() const {return src && composed;} + + // + explicit inline operator SBufferBinding() const + { + if (*this) + return {.offset=src.offset,.buffer=core::smart_refctd_ptr(src.buffer)}; + return {}; + } + + inline uint64_t getElementCount() const + { + if (!this->operator bool()) + return 0ull; + const auto stride = composed.getStride(); + if (stride==0) + return 0ull; + return src.size/stride; + } + + // + template requires (std::is_same_v && std::is_same_v) + inline const void* getPointer(const Index elIx=0) const + { + if (*this) + return reinterpret_cast(src.buffer->getPointer())+src.offset+elIx*composed.getStride(); + return nullptr; + } + template requires (std::is_same_v && std::is_same_v) + inline void* getPointer(const Index elIx=0) + { + if (*this) + return reinterpret_cast(src.buffer->getPointer())+src.offset+elIx*composed.getStride(); + return nullptr; + } + + // + template requires (hlsl::concepts::Vector && std::is_same_v && std::is_same_v) + inline bool decodeElement(const Index elIx, V& v) const + { + if (!composed.isFormatted()) + return false; + using code_t = std::conditional_t,hlsl::float64_t,std::conditional_t,int64_t,uint64_t>>; + code_t tmp[4]; + if (const auto* src=getPointer(elIx); src) + { + const void* srcArr[4] = {src,nullptr}; + assert(!isScaledFormat(composed.format)); // handle this by improving the decode functions, not adding workarounds here + if (decodePixels(composed.format,srcArr,tmp,0,0)) + { + using traits = hlsl::vector_traits; + const auto range = composed.getRange>(); + for (auto i=0u; i requires (hlsl::concepts::Vector && std::is_same_v && std::is_same_v) + inline void encodeElement(const Index elIx, const V& v) + { + if (!composed.isFormatted()) + return false; + void* const out = getPointer(elIx); + if (!out) + return false; + using traits = hlsl::vector_traits; + using code_t = std::conditional_t,hlsl::float64_t,std::conditional_t,int64_t,uint64_t>>; + code_t tmp[traits::Dimension]; + const auto range = composed.getRange(); + for (auto i=0u; i(composed.format,out,tmp)) + return true; + return false; + } + + // + inline SDataView clone(uint32_t _depth=~0u) const + { + SDataView retval; + retval.composed = composed; + retval.src.offset = src.offset; + retval.src.size = src.size; + if (_depth) + retval.src.buffer = core::smart_refctd_ptr_static_cast(src.buffer->clone(_depth)); + else + retval.src.buffer = core::smart_refctd_ptr(src.buffer); + return retval; + } + + SDataViewBase composed = {}; + SBufferRange src = {}; + }; + // + inline const SDataView& getPositionView() const {return m_positionView;} + + // depends on indexing, primitive type, etc. + virtual uint64_t getPrimitiveCount() const = 0; + + // This is the upper bound on the local Joint IDs that the geometry uses + virtual uint32_t getJointCount() const = 0; + + // + inline bool isSkinned() const {return getJointCount()>0;} + + // Providing Per-Joint Bind-Pose-Space AABBs is optional for a skinned geometry + inline const SDataView* getJointOBBView() const + { + if (isSkinned()) + return &m_jointOBBView; + return nullptr; + } + + protected: + virtual inline ~IGeometry() = default; + + // + inline bool setJointOBBView(SDataView&& view) + { + // want to set, not clear the AABBs + if (view) + { + // An OBB is a affine transform of a [0,1]^3 unit cube to the Oriented Bounding Box + // Needs to be formatted, each element is a row of that matrix + if (!view.composed.isFormatted() || getFormatChannelCount(view.composed.format)!=4) + return false; + // The range doesn't need to be exact, just large enough + if (view.getElementCount() +class IIndexableGeometry : public IGeometry +{ + protected: + using SDataView = IGeometry::SDataView; + + public: + // index buffer is optional so no override of `valid()` + + inline const SDataView& getIndexView() const {return m_indexView;} + + inline const uint64_t getIndexCount() const + { + return m_indexView.getElementCount(); + } + + protected: + virtual inline ~IIndexableGeometry() = default; + + // Needs to be hidden because ICPU base class shall check mutability + inline bool setIndexView(SDataView&& view) + { + if (!view || view.composed.isFormattedScalarInteger()) + { + m_indexView = std::move(view); + return true; + } + return false; + } + + // + SDataView m_indexView = {}; +}; + +} + +// +namespace nbl::core +{ +template +struct blake3_hasher::update_impl +{ + static inline void __call(blake3_hasher& hasher, const asset::IGeometryBase::SDataViewBase& input) + { + hasher << input.stride; + hasher << input.format; + hasher << input.rangeFormat; + input.visitAABB([&hasher](auto& aabb)->void{hasher.update(&aabb,sizeof(aabb));}); + } +}; +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/IGeometryCollection.h b/include/nbl/asset/IGeometryCollection.h new file mode 100644 index 0000000000..cd72990365 --- /dev/null +++ b/include/nbl/asset/IGeometryCollection.h @@ -0,0 +1,129 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_GEOMETRY_COLLECTION_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_COLLECTION_H_INCLUDED_ + + +#include "nbl/asset/IPolygonGeometry.h" + + +namespace nbl::asset +{ +// Collection of geometries of the same type (but e.g. with different formats or transforms) +template +class NBL_API2 IGeometryCollection : public virtual core::IReferenceCounted +{ + public: + using SDataView = IGeometry::SDataView; + + // + inline const auto& getAABB() const {return m_aabb;} + + // + struct SGeometryReference + { + inline operator bool() const + { + if (!geometry) + return false; + if (jointRedirectView.src) + { + if (!jointRedirectView.isFormattedScalarInteger()) + return false; + if (jointRedirectView.getElementCount()getJointCount()*2) + return false; + } + else + return true; + } + + inline bool hasTransform() const {return !core::isnan(transform[0][0]);} + + hlsl::float32_t3x4 transform = hlsl::float32_t3x4(std::numeric_limits::quiet_NaN()); + core::smart_refctd_ptr> geometry = {}; + // The geometry may be using a smaller set of joint/bone IDs which need to be remapped to a larger or common skeleton + // Ignored if this geometry collection is not skinned or the `geometry` doesn't have a weight view. + // If not provided, its treated as-if an iota {0,1,2,...} view was provided + SDataView jointRedirectView = {}; + }; + inline const core::vector& getGeometries() const {return m_geometries;} + + // This is for the whole geometry collection, geometries remap to those. + // Each element is a row of an affine transformation matrix + inline uint32_t getJointCount() const {return m_inverseBindPoseView.getElementCount()/3;} + + // + inline bool isSkinned() const {return getJointCount()>0;} + // View of matrices being the inverse bind pose + inline const SDataView& getInverseBindPoseView() const {return m_inverseBindPoseView;} + + + protected: + virtual ~IGeometryCollection() = default; + + // + inline core::vector& getGeometries() {return m_geometries;} + + // returns whether skinning was enabled or disabled + inline bool setSkin(SDataView&& inverseBindPoseView, SDataView&& jointAABBView) + { + // disable skinning + m_inverseBindPoseView = {}; + m_jointAABBView = {}; + // need a format with one row per element + const auto ibpFormat = inverseBindPoseView.composed.format; + if (!inverseBindPoseView || !inverseBindPoseView.composed.isFormatted() || getFormatChannelCount(ibpFormat)!=4) + return false; + const auto matrixRowCount = inverseBindPoseView.getElementCount(); + // and 3 elements per matrix + if (matrixRowCount==0 || (matrixRowCount%3)!=0) + return false; + const auto jointCount = matrixRowCount/3; + // ok now check the AABB stuff + if (jointAABBView) + { + // needs to be formatted + if (!jointAABBView.composed.isFormatted()) + return false; + // each element is a AABB vertex, so need 2 per joint + if (jointAABBView.getElementCount()!=jointCount*2) + return false; + } + m_inverseBindPoseView = std::move(inverseBindPoseView); + m_jointAABBView = std::move(jointAABBView); + return true; + } + + // need to be protected because of the mess around `transform` requires us to provide diffferent signatures for ICPUGeometryCollection and IGPUGeometryCollection + using BLASTriangles = IBottomLevelAccelerationStructure::Triangles>; + template// requires std::is_same_v()),decltype(BLASTriangles&)> + inline Iterator exportForBLAS(Iterator out, Callback& setTransform) const + { + for (const auto& ref : m_geometries) + { + // not a polygon geometry + const auto* geo = ref.geometry.get(); + if (geo->getPrimitiveType()==IGeometryBase::EPrimitiveType::Polygon) + continue; + const auto* polyGeo = static_cast*>(geo); + *out = polyGeo->exportForBLAS(); + if (out->vertexData[0]) + out++; + } + return out; + } + + + // For the entire collection, as always it should NOT include any geometry which is affected by a joint. + IGeometryBase::SAABBStorage m_aabb; + SDataView m_inverseBindPoseView = {}; + // The AABBs gathered from all geometries (optional) and are in "bone-space" so there's no need for OBB option, + // joint influence is usually aligned to the covariance matrix of geometry affected by it. + SDataView m_jointAABBView = {}; + // + core::vector m_geometries; +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/IMesh.h b/include/nbl/asset/IMesh.h deleted file mode 100644 index 8f1eb0b8f2..0000000000 --- a/include/nbl/asset/IMesh.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_MESH_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_H_INCLUDED__ - -#include "nbl/core/IReferenceCounted.h" -#include "aabbox3d.h" - -namespace nbl -{ -namespace asset -{ - //! Class which holds the geometry of an object. - /** An IMesh is nothing more than a collection of some mesh buffers - (IMeshBuffer). - */ - template - class IMesh : public virtual core::IReferenceCounted - { - protected: - //! The cached bounding box of this mesh - core::aabbox3d m_cachedBoundingBox; - - virtual ~IMesh() {} - - public: - //! Get iterator range of the attached meshbuffers - virtual core::SRange getMeshBuffers() const = 0; - virtual core::SRange getMeshBuffers() = 0; - - //! Get an axis aligned bounding box of the mesh. - /** \return Bounding box of this mesh. */ - inline const core::aabbox3df& getBoundingBox() const { return m_cachedBoundingBox; } - - //! - inline virtual void setBoundingBox(const core::aabbox3df& newBoundingBox) - { - m_cachedBoundingBox = newBoundingBox; - } - }; - -} // end namespace asset -} // end namespace nbl - -#endif - diff --git a/include/nbl/asset/IMeshBuffer.h b/include/nbl/asset/IMeshBuffer.h deleted file mode 100644 index a4f1b895dc..0000000000 --- a/include/nbl/asset/IMeshBuffer.h +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright (C) 2018-2021 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_ASSET_I_MESH_BUFFER_H_INCLUDED_ -#define _NBL_ASSET_I_MESH_BUFFER_H_INCLUDED_ - - -#include "nbl/asset/IRenderpassIndependentPipeline.h" -#include "nbl/asset/ECommonEnums.h" - -#include - -namespace nbl::asset -{ - -template -class IMeshBuffer : public virtual core::IReferenceCounted -{ - public: - _NBL_STATIC_INLINE_CONSTEXPR size_t MAX_PUSH_CONSTANT_BYTESIZE = 128u; - - _NBL_STATIC_INLINE_CONSTEXPR size_t MAX_VERTEX_ATTRIB_COUNT = SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT; - _NBL_STATIC_INLINE_CONSTEXPR size_t MAX_ATTR_BUF_BINDING_COUNT = SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; - - protected: - virtual ~IMeshBuffer() = default; - - alignas(32) core::aabbox3df boundingBox; - - // TODO: format, stride, input rate - SBufferBinding m_vertexBufferBindings[MAX_ATTR_BUF_BINDING_COUNT]; - SBufferBinding m_indexBufferBinding; - - //! Skin - SBufferBinding m_inverseBindPoseBufferBinding,m_jointAABBBufferBinding; - - //! Descriptor set which goes to set=3 - core::smart_refctd_ptr m_descriptorSet; - - alignas(64) uint8_t m_pushConstantsData[MAX_PUSH_CONSTANT_BYTESIZE]{};//by putting m_pushConstantsData here, alignas(64) takes no extra place - - // TODO: remove descriptor set, pipeline & instancing info - //! Pipeline for drawing - core::smart_refctd_ptr m_pipeline; - - // draw params - uint32_t indexCount = 0u; - uint32_t instanceCount = 1u; - int32_t baseVertex = 0; - uint32_t baseInstance = 0u; - - // others - uint32_t jointCount : 11; - uint32_t maxJointsPerVx : 3; - uint32_t indexType : 2; - - public: - IMeshBuffer() : jointCount(0u), maxJointsPerVx(0u), indexType(EIT_UNKNOWN) {} - //! Constructor. - IMeshBuffer(core::smart_refctd_ptr&& _pipeline, - core::smart_refctd_ptr&& _ds, - SBufferBinding _vtxBindings[MAX_ATTR_BUF_BINDING_COUNT], - SBufferBinding&& _indexBinding - ) : boundingBox(), m_indexBufferBinding(std::move(_indexBinding)), - m_inverseBindPoseBufferBinding(), m_jointAABBBufferBinding(), - m_descriptorSet(std::move(_ds)), m_pipeline(std::move(_pipeline)), - indexCount(0u), instanceCount(1u), baseVertex(0), baseInstance(0u), - jointCount(0u), maxJointsPerVx(0u), indexType(EIT_UNKNOWN) - { - if (_vtxBindings) - std::copy(_vtxBindings, _vtxBindings+MAX_ATTR_BUF_BINDING_COUNT, m_vertexBufferBindings); - } - - inline bool isAttributeEnabled(uint32_t attrId) const - { - if (attrId >= MAX_VERTEX_ATTRIB_COUNT) - return false; - if (!m_pipeline) - return false; - - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - if (!(vtxInputParams.enabledAttribFlags & (1u<= MAX_ATTR_BUF_BINDING_COUNT) - return false; - if (!m_pipeline) - return false; - - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - if (!(vtxInputParams.enabledBindingFlags & (1u<getCachedCreationParams().vertexInput; - return vtxInputParams.attributes[attrId].binding; - } - inline E_FORMAT getAttribFormat(uint32_t attrId) const - { - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - return static_cast(vtxInputParams.attributes[attrId].format); - } - inline uint32_t getAttribStride(uint32_t attrId) const - { - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - const uint32_t bnd = getBindingNumForAttribute(attrId); - return vtxInputParams.bindings[bnd].stride; - } - inline uint32_t getAttribOffset(uint32_t attrId) const - { - const auto& vtxInputParams = m_pipeline->getCachedCreationParams().vertexInput; - return vtxInputParams.attributes[attrId].relativeOffset; - } - - inline SBufferBinding& getAttribBoundBuffer(uint32_t attrId) - { - const uint32_t bnd = getBindingNumForAttribute(attrId); - return m_vertexBufferBindings[bnd]; - } - inline const SBufferBinding& getAttribBoundBuffer(uint32_t attrId) const - { - const uint32_t bnd = getBindingNumForAttribute(attrId); - return reinterpret_cast&>(m_vertexBufferBindings[bnd]); - } - - inline const SBufferBinding* getVertexBufferBindings() - { - return m_vertexBufferBindings; - } - inline const SBufferBinding* getVertexBufferBindings() const - { - return reinterpret_cast*>(m_vertexBufferBindings); - } - - inline const SBufferBinding& getIndexBufferBinding() - { - return m_indexBufferBinding; - } - inline const SBufferBinding& getIndexBufferBinding() const - { - return reinterpret_cast&>(m_indexBufferBinding); - } - - inline const SBufferBinding& getInverseBindPoseBufferBinding() - { - return m_inverseBindPoseBufferBinding; - } - inline const SBufferBinding& getInverseBindPoseBufferBinding() const - { - return reinterpret_cast&>(m_inverseBindPoseBufferBinding); - } - - inline const SBufferBinding& getJointAABBBufferBinding() - { - return m_jointAABBBufferBinding; - } - inline const SBufferBinding& getJointAABBBufferBinding() const - { - return reinterpret_cast&>(m_jointAABBBufferBinding); - } - - virtual inline bool setVertexBufferBinding(SBufferBinding&& bufferBinding, uint32_t bindingIndex) - { - if (bindingIndex >= MAX_ATTR_BUF_BINDING_COUNT) - return false; - - m_vertexBufferBindings[bindingIndex] = std::move(bufferBinding); - - return true; - } - - virtual inline void setIndexBufferBinding(SBufferBinding&& bufferBinding) - { - // assert(isMutable()); - - m_indexBufferBinding = std::move(bufferBinding); - } - - virtual inline void setAttachedDescriptorSet(core::smart_refctd_ptr&& descriptorSet) - { - //assert(isMutable()); - m_descriptorSet = std::move(descriptorSet); - } - - virtual inline void setPipeline(core::smart_refctd_ptr&& pipeline) - { - //assert(isMutable()); - m_pipeline = std::move(pipeline); - } - - inline uint64_t getAttribCombinedOffset(uint32_t attrId) const - { - const auto& buf = getAttribBoundBuffer(attrId); - return buf.offset+static_cast(getAttribOffset(attrId)); - } - - //! Returns bound on JointID in the vertex attribute - inline auto getJointCount() const { return jointCount; } - - //! Returns max joint influences - inline auto getMaxJointsPerVertex() const { return maxJointsPerVx; } - - //! - virtual inline bool isSkinned() const - { - return jointCount>0u && maxJointsPerVx>0u && m_inverseBindPoseBufferBinding.buffer && - m_inverseBindPoseBufferBinding.offset+jointCount*sizeof(core::matrix3x4SIMD)<=m_inverseBindPoseBufferBinding.buffer->getSize(); - } - - //! - virtual inline bool setSkin( - SBufferBinding&& _inverseBindPoseBufferBinding, - SBufferBinding&& _jointAABBBufferBinding, - const uint32_t _jointCount, const uint32_t _maxJointsPerVx - ) - { - if (!_inverseBindPoseBufferBinding.buffer || !_jointAABBBufferBinding.buffer || _jointCount==0u) - return false; - - // a very arbitrary constraint - if (_maxJointsPerVx==0u || _maxJointsPerVx>4u) - return false; - - if (_inverseBindPoseBufferBinding.offset+_jointCount*sizeof(core::matrix3x4SIMD)>_inverseBindPoseBufferBinding.buffer->getSize()) - return false; - - m_inverseBindPoseBufferBinding = std::move(_inverseBindPoseBufferBinding); - m_jointAABBBufferBinding = std::move(_jointAABBBufferBinding); - jointCount = _jointCount; - maxJointsPerVx = _maxJointsPerVx; - return true; - } - - //! - inline const DescSetType* getAttachedDescriptorSet() const - { - return m_descriptorSet.get(); - } - - //! - inline const PipelineType* getPipeline() const - { - return m_pipeline.get(); - } - - //! Get type of index data which is stored in this meshbuffer. - /** \return Index type of this buffer. */ - inline E_INDEX_TYPE getIndexType() const {return static_cast(indexType);} - inline void setIndexType(const E_INDEX_TYPE type) - { - indexType = type; - } - - //! Get amount of indices in this meshbuffer. - /** \return Number of indices in this buffer. */ - inline auto getIndexCount() const {return indexCount;} - //! It sets amount of indices - value that is being passed to glDrawArrays as vertices amount or to glDrawElements as index amount. - /** @returns Whether set amount exceeds mapped buffer's size. Regardless of result the amount is set. */ - inline bool setIndexCount(const uint32_t newIndexCount) - { - indexCount = newIndexCount; - if (m_indexBufferBinding.buffer) - { - switch (indexType) - { - case EIT_16BIT: - return indexCount*sizeof(uint16_t)+m_indexBufferBinding.offset < m_indexBufferBinding.buffer->getSize(); - case EIT_32BIT: - return indexCount*sizeof(uint32_t)+m_indexBufferBinding.offset < m_indexBufferBinding.buffer->getSize(); - default: - return false; - } - } - - return true; - } - - //! Accesses base vertex number. - /** @returns base vertex number. */ - inline int32_t getBaseVertex() const {return baseVertex;} - //! Sets base vertex. - inline void setBaseVertex(const int32_t baseVx) - { - baseVertex = baseVx; - } - - inline uint32_t getInstanceCount() const {return instanceCount;} - inline void setInstanceCount(const uint32_t count) - { - instanceCount = count; - } - - inline uint32_t getBaseInstance() const {return baseInstance;} - inline void setBaseInstance(const uint32_t base) - { - baseInstance = base; - } - - - //! Get the axis aligned bounding box of this meshbuffer. - /** \return Axis aligned bounding box of this buffer. */ - inline const core::aabbox3df& getBoundingBox() const {return boundingBox;} - - //! Set axis aligned bounding box - /** \param box User defined axis aligned bounding box to use - for this buffer. */ - inline virtual void setBoundingBox(const core::aabbox3df& box) - { - boundingBox = box; - } - - uint8_t* getPushConstantsDataPtr() { return m_pushConstantsData; } - const uint8_t* getPushConstantsDataPtr() const { return m_pushConstantsData; } -}; - -} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/IMorphTargets.h b/include/nbl/asset/IMorphTargets.h new file mode 100644 index 0000000000..14265aa71a --- /dev/null +++ b/include/nbl/asset/IMorphTargets.h @@ -0,0 +1,83 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_MORPH_TARGETS_H_INCLUDED_ +#define _NBL_ASSET_I_MORPH_TARGETS_H_INCLUDED_ + + +#include "nbl/asset/IGeometryCollection.h" + + +namespace nbl::asset +{ +// Unlike glTF we don't require same index buffers and maintaining isomorphisms between primitives/vertices in different geometries. But most of the use cases would have such mappings. +// The semantics are really up to you, these are just collections of Geometries which can be swapped out or interpolated between. +// Note: A LoD set can also be viewed as a morph shape per LoD, while a Motion Blur BLAS can be viewed as representing the interval between two Morph Targets. +template +class NBL_API2 IMorphTargets : public virtual core::IReferenceCounted +{ + public: + struct index_t + { + explicit inline index_t(uint32_t _value) : value(_value) {} + + inline operator bool() const {return value!=(~0u);} + + uint32_t value = ~0u; + }; + + inline uint32_t getTargetCount() const + { + return static_cast(m_targets.size()); + } + + template requires std::is_floating_point_v + struct SInterpolants + { + index_t indices[Degree]; + Scalar weights[Degree-1]; + }; + + template requires std::is_floating_point_v + inline SInterpolants getLinearBlend(const Scalar blend) const + { + SInterpolants retval; + if (!m_targets.empty()) + { + const Scalar maxMorph = getTargetCount(); + retval.indices[0] = index_t(hlsl::clamp(blend,Scalar(0),maxMorph)); + retval.indices[1] = index_t(hlsl::min(retval.indices[0].value+Scalar(1),maxMorph)); + retval.weights[0] = blend-Scalar(retval.indices[0].value); + } + return retval; + } + + struct STarget + { + inline operator bool() const + { + return geoCollection && (!jointRedirectView || jointRedirectView.composed.isFormattedScalarInteger()); + } + + core::smart_refctd_ptr geoCollection = {}; + // The geometry may be using a smaller set of joint/bone IDs which need to be remapped to a larger or common skeleton. + // Ignored if the collection is not skinned. + GeometryCollection::SDataView jointRedirectView = {}; + }; + inline const core::vector& getTargets() const {return m_targets;} + + protected: + virtual ~IMorphTargets() = default; + + // + inline core::vector& getTargets() {return m_targets;} + + // TODO: utility to make motion-blur IBottomLevelAccelerationStructure::Triangles from two targets + + // + core::vector m_targets; + // no point keeping an overall AABB, because the only reason to do that is to skip animation/indexing logic all together +}; +} + +#endif \ No newline at end of file diff --git a/include/nbl/asset/IPolygonGeometry.h b/include/nbl/asset/IPolygonGeometry.h new file mode 100644 index 0000000000..1b4f64a550 --- /dev/null +++ b/include/nbl/asset/IPolygonGeometry.h @@ -0,0 +1,275 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_POLYGON_GEOMETRY_H_INCLUDED_ +#define _NBL_ASSET_I_POLYGON_GEOMETRY_H_INCLUDED_ + + +#include "nbl/asset/IGeometry.h" +#include "nbl/asset/RasterizationStates.h" +#include "nbl/asset/IAccelerationStructure.h" + +#include + +namespace nbl::asset +{ +// +class IPolygonGeometryBase : public virtual core::IReferenceCounted +{ + public: + // + class IIndexingCallback + { + public: + // how many vertices per polygon + inline uint8_t degree() const + { + const auto retval = degree_impl(); + assert(retval>0); + return retval; + } + // at which we consume indices for each new polygon + inline uint8_t rate() const + { + const auto retval = rate_impl(); + assert(retval>0 && retval<=degree()); + return retval; + } + + template requires (sizeof(OutT)<8 && hlsl::concepts::UnsignedIntegralScalar) + struct SContext final + { + // `indexOfIndex` is somewhat of a baseIndex + template + inline void streamOut(const uint32_t indexOfIndex, const Range& permutation) + { + auto& typedOut = reinterpret_cast(out); + if (indexBuffer) + switch (indexSize) + { + case 1: + for (const auto relIx : permutation) + *(typedOut++) = reinterpret_cast(indexBuffer)[indexOfIndex+relIx]; + break; + case 2: + for (const auto relIx : permutation) + *(typedOut++) = reinterpret_cast(indexBuffer)[indexOfIndex+relIx]; + break; + case 4: + for (const auto relIx : permutation) + *(typedOut++) = reinterpret_cast(indexBuffer)[indexOfIndex+relIx]; + break; + default: + assert(false); + break; + } + else + for (const auto relIx : permutation) + *(typedOut++) = indexOfIndex+relIx; + } + + // always the base pointer, doesn't get advanced + const void* const indexBuffer; + const uint64_t indexSize : 3; + const uint64_t beginPrimitive : 30; + const uint64_t endPrimitive : 31; + void* out; + }; + // could have been a static if not virtual + virtual void operator()(SContext& ctx) const = 0; + virtual void operator()(SContext& ctx) const = 0; + virtual void operator()(SContext& ctx) const = 0; + + // default is unknown + virtual inline E_PRIMITIVE_TOPOLOGY knownTopology() const {return static_cast(~0);} + + protected: + virtual uint8_t degree_impl() const = 0; + virtual uint8_t rate_impl() const = 0; + }; + // + NBL_API2 static IIndexingCallback* PointList(); + NBL_API2 static IIndexingCallback* LineList(); + NBL_API2 static IIndexingCallback* TriangleList(); + NBL_API2 static IIndexingCallback* QuadList(); + // TODO: Adjacency, Patch, etc. + NBL_API2 static IIndexingCallback* TriangleStrip(); + NBL_API2 static IIndexingCallback* TriangleFan(); + + // This should be a pointer to a stateless singleton (think of it more like a dynamic enum/template than anything else) + inline const IIndexingCallback* getIndexingCallback() const {return m_indexing;} + + protected: + virtual inline ~IPolygonGeometryBase() = default; + + // indexing callback cannot be cleared + inline bool setIndexingCallback(IIndexingCallback* indexing) + { + if (!indexing) + return false; + const auto deg = m_indexing->degree(); + if (deg==0 || m_indexing->rate()==0 || m_indexing->rate()>deg) + return false; + m_indexing = indexing; + return true; + } + + // + const IIndexingCallback* m_indexing = nullptr; +}; + +// Don't want to overengineer, support for variable vertex count (order) polgyon meshes is not encouraged or planned. +// If you want different polygon types in same model, bucket the polgyons into separate geometries and reunite in a single collection. +template +class IPolygonGeometry : public IIndexableGeometry, public IPolygonGeometryBase +{ + using base_t = IIndexableGeometry; + + protected: + using EPrimitiveType = base_t::EPrimitiveType; + using SDataView = base_t::SDataView; + using BLASTriangles = IBottomLevelAccelerationStructure::Triangles>; + + public: + // + virtual inline bool valid() const override + { + if (!base_t::valid()) + return false; + if (!m_indexing) + return false; + // there needs to be at least one vertex to reference (it also needs to be formatted) + const auto& positionBase = base_t::m_positionView.composed; + const auto vertexCount = base_t::m_positionView.getElementCount(); + if (vertexCount==0 || !positionBase.isFormatted()) + return false; + if (m_normalView && m_normalView.getElementCount()degree(); + if (vertexReferenceCountrate()+1; + } + + // For when the geometric normal of the patch isn't enough and you want interpolated custom normals + inline const SDataView& getNormalView() const {return m_normalView;} + + // Its also a Max Joint ID that `m_jointWeightViews` can reference + inline uint32_t getJointCount() const override final + { + return m_jointCount; + } + + // SoA instead of AoS, first component is the first bone influece, etc. + struct SJointWeight + { + // one thing this doesn't check is whether every vertex has a weight and index + inline operator bool() const {return indices && isIntegerFormat(indices.composed.format) && weights && weights.composed.isFormatted() && indices.getElementCount()==weights.getElementCount();} + + SDataView indices; + // Assumption is that only non-zero weights are present, which is why the joints are indexed (sparseness) + // Zero weights are acceptable but only if they form a contiguous block including the very last component of the very last weight view + SDataView weights; + }; + // It's a vector in case you need more than 4 influences per bone + inline const core::vector& getJointWeightViews() const {return m_jointWeightViews;} + + // For User defined semantics + inline const core::vector& getAuxAttributeViews() const {return m_auxAttributeViews;} + + + // Does not set the `transform` or `geometryFlags` fields, because it doesn't care about it. + // Also won't set second set of vertex data, opacity mipmaps, etc. + inline BLASTriangles exportForBLAS() const + { + BLASTriangles retval = {}; + // must be a triangle list, but don't want to compare pointers + if (m_indexing && m_indexing->knownTopology()==EPT_TRIANGLE_LIST)// && m_indexing->degree() == TriangleList()->degree() && m_indexing->rate() == TriangleList->rate()) + { + auto indexType = EIT_UNKNOWN; + // disallowed index format + if (base_t::m_indexView) + { + switch (base_t::m_indexView.composed.format) + { + case EF_R16_UINT: + indexType = EIT_16BIT; + break; + case EF_R32_UINT: [[fallthrough]]; + indexType = EIT_32BIT; + break; + default: + break; + } + if (indexType==EIT_UNKNOWN) + return retval; + } + retval.vertexData[0] = base_t::m_positionView.src; + retval.indexData = base_t::m_indexView.src; + retval.maxVertex = base_t::m_positionView.getElementCount() - 1; + retval.vertexStride = base_t::m_positionView.composed.getStride(); + retval.vertexFormat = base_t::m_positionView.composed.format; + retval.indexType = indexType; + } + return retval; + } + + protected: + virtual ~IPolygonGeometry() = default; + + // 64bit indices are just too much to deal with in all the other code + // Also if you have more than 2G vertex references in a single geometry there's something wrong with your architecture + // Note that this still allows 6GB vertex attribute streams (assuming at least 3 bytes for a postion) + inline bool setIndexView(SDataView&& view) + { + if (view) + { + const auto format = view.composed.format; + if (!view.composed.isFormattedScalarInteger() || format == EF_R64_UINT || format == EF_R64_SINT) + return false; + if (view.getElementCount()>(1u<<31)) + return false; + } + base_t::m_indexView = std::move(view); + return true; + } + + // + core::vector m_jointWeightViews = {}; + // + core::vector m_auxAttributeViews = {}; + // + SDataView m_normalView = {}; + // + uint32_t m_jointCount = 0; +}; + +} +#endif \ No newline at end of file diff --git a/include/nbl/asset/IRenderpassIndependentPipeline.h b/include/nbl/asset/IRenderpassIndependentPipeline.h deleted file mode 100644 index feeaff7c99..0000000000 --- a/include/nbl/asset/IRenderpassIndependentPipeline.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_H_INCLUDED_ -#define _NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_H_INCLUDED_ - - -#include "nbl/macros.h" - -#include "nbl/core/declarations.h" - -#include "nbl/asset/IGraphicsPipeline.h" - -#include - - -namespace nbl::asset -{ - -//! Deprecated class but needs to stay around till Material Compiler 2 -class IRenderpassIndependentPipeline -{ - public: - struct SCachedCreationParams final - { - SVertexInputParams vertexInput = {}; - SPrimitiveAssemblyParams primitiveAssembly = {}; - SRasterizationParams rasterization = {}; - SBlendParams blend = {}; - }; - - inline const SCachedCreationParams& getCachedCreationParams() const {return m_cachedParams;} - - constexpr static inline size_t GRAPHICS_SHADER_STAGE_COUNT = 5u; - - protected: - IRenderpassIndependentPipeline(const SCachedCreationParams& _cachedParams) : m_cachedParams(_cachedParams) {} - virtual ~IRenderpassIndependentPipeline() = default; - - SCachedCreationParams m_cachedParams; -}; - -} -#endif \ No newline at end of file diff --git a/include/nbl/asset/asset.h b/include/nbl/asset/asset.h index 84d9b9ccd2..80c5374806 100644 --- a/include/nbl/asset/asset.h +++ b/include/nbl/asset/asset.h @@ -10,18 +10,14 @@ #include "nbl/system/declarations.h" #include "nbl/system/definitions.h" // TODO: split `asset.h` into decl and def -// utils -#include "nbl/asset/asset_utils.h" - // format #include "nbl/asset/format/EFormat.h" #include "nbl/asset/format/convertColor.h" #include "nbl/asset/format/decodePixels.h" #include "nbl/asset/format/encodePixels.h" -// base +// buffers #include "nbl/asset/ICPUBuffer.h" -#include "nbl/asset/IMesh.h" //depr // images #include "nbl/asset/ICPUImage.h" @@ -51,31 +47,25 @@ #include "nbl/asset/ICPUAnimationLibrary.h" #include "nbl/asset/ICPUSkeleton.h" -// meshes -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/ICPUMesh.h" -#include "nbl/asset/utils/IGeometryCreator.h" -// #include "nbl/asset/utils/IMeshPacker.h" +// geometry +#include "nbl/asset/utils/CGeometryCreator.h" +#include "nbl/asset/ICPUGeometryCollection.h" +#include "nbl/asset/ICPUMorphTargets.h" // manipulation + reflection + introspection -#include "nbl/asset/utils/IMeshManipulator.h" +#include "nbl/asset/utils/CSmoothNormalGenerator.h" #include "nbl/asset/IAssetManager.h" // importexport #include "nbl/asset/interchange/IAssetLoader.h" #include "nbl/asset/interchange/IImageLoader.h" -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" +#include "nbl/asset/interchange/IGeometryLoader.h" #include "nbl/asset/interchange/IAssetWriter.h" #include "nbl/asset/interchange/IImageWriter.h" #include "nbl/asset/metadata/COpenEXRMetadata.h" #include "nbl/asset/metadata/CMTLMetadata.h" -#include "nbl/asset/metadata/COBJMetadata.h" #include "nbl/asset/metadata/CPLYMetadata.h" #include "nbl/asset/metadata/CSTLMetadata.h" -//VT -// #include "nbl/asset/utils/CCPUMeshPackerV1.h" -// #include "nbl/asset/utils/CCPUMeshPackerV2.h" - #endif diff --git a/include/nbl/asset/asset_utils.h b/include/nbl/asset/asset_utils.h deleted file mode 100644 index 8e4e35a733..0000000000 --- a/include/nbl/asset/asset_utils.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_UTILS_H_INCLUDED__ -#define __NBL_ASSET_UTILS_H_INCLUDED__ - -// TODO: this whole header should die - -#include "nbl/asset/ICPUBuffer.h" -#include - -namespace nbl { -namespace asset -{ - -inline void fillBufferWithDWORD(ICPUBuffer* _buf, uint32_t _val) -{ - const size_t dwCnt = _buf->getSize()/4ull; - const size_t rem = _buf->getSize()-(dwCnt*4ull); - - uint32_t* dwptr = reinterpret_cast(_buf->getPointer()); - std::fill(dwptr, dwptr+dwCnt, _val); - memcpy(dwptr+dwCnt, &_val, rem); -} - -inline void fillBufferWithDeadBeef(ICPUBuffer* _buf) -{ - fillBufferWithDWORD(_buf, 0xdeadbeefu); -} - -#include "nbl/nblpack.h" -//! Designed for use with interface blocks declared with `layout (row_major, std140)` -// TODO: change members to core::matrix3x4SIMD and core::matrix4SIMD -struct SBasicViewParameters -{ - float MVP[4*4]; - //! Might be used for Model matrix just as well - float MV[3*4]; - //! 3x3 but each row is padded to 4 floats (16 bytes), so that actually we have 3x4 - //! so we have 3 floats free for use (last element of each row) which can be used for storing some vec3 (most likely camera position) - //! This vec3 is accessible in GLSL by accessing 4th column with [] operator - float NormalMat[3*3+3]; -} PACK_STRUCT; -#include "nbl/nblunpack.h" - -static_assert(sizeof(SBasicViewParameters) == sizeof(float) * (4 * 4 + 2 * 3 * 4)); - -}} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/format/decodePixels.h b/include/nbl/asset/format/decodePixels.h index bbe93c40b9..dd887a08f5 100644 --- a/include/nbl/asset/format/decodePixels.h +++ b/include/nbl/asset/format/decodePixels.h @@ -1680,7 +1680,6 @@ namespace asset case asset::EF_G8_B8R8_2PLANE_422_UNORM: decodePixels(_pix, _output, _blockX, _blockY); return true; case asset::EF_G8_B8_R8_3PLANE_444_UNORM: decodePixels(_pix, _output, _blockX, _blockY); return true; default: - assert(!"Format decode not supported"); return false; } } diff --git a/include/nbl/asset/interchange/IAssetLoader.h b/include/nbl/asset/interchange/IAssetLoader.h index 5f85cf2c9d..38480b429e 100644 --- a/include/nbl/asset/interchange/IAssetLoader.h +++ b/include/nbl/asset/interchange/IAssetLoader.h @@ -14,7 +14,7 @@ namespace nbl::asset { -class IMeshManipulator; +class CPolygonGeometryManipulator; //! A class automating process of loading Assets from resources, eg. files /** @@ -117,7 +117,7 @@ class NBL_API2 IAssetLoader : public virtual core::IReferenceCounted const uint8_t* decryptionKey; E_CACHING_FLAGS cacheFlags; E_LOADER_PARAMETER_FLAGS loaderFlags; //!< Flags having an impact on extraordinary tasks during loading process - IMeshManipulator* meshManipulatorOverride = nullptr; //!< pointer used for specifying custom mesh manipulator to use, if nullptr - default mesh manipulator will be used + CPolygonGeometryManipulator* meshManipulatorOverride = nullptr; //!< pointer used for specifying custom mesh manipulator to use, if nullptr - default mesh manipulator will be used std::filesystem::path workingDirectory = ""; system::logger_opt_ptr logger; }; diff --git a/include/nbl/asset/interchange/IAssetWriter.h b/include/nbl/asset/interchange/IAssetWriter.h index 8c41f3f327..694053df5e 100644 --- a/include/nbl/asset/interchange/IAssetWriter.h +++ b/include/nbl/asset/interchange/IAssetWriter.h @@ -1,18 +1,17 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_ASSET_WRITER_H_INCLUDED_ +#define _NBL_ASSET_I_ASSET_WRITER_H_INCLUDED_ -#ifndef __NBL_ASSET_I_ASSET_WRITER_H_INCLUDED__ -#define __NBL_ASSET_I_ASSET_WRITER_H_INCLUDED__ #include "nbl/system/IFile.h" #include "nbl/system/ILogger.h" #include "nbl/asset/IAsset.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { //! Writing flags @@ -192,6 +191,5 @@ class IAssetWriter : public virtual core::IReferenceCounted static void getDefaultOverride(IAssetWriterOverride*& _out) { _out = &s_defaultOverride; } }; -}} //nbl::asset - +} //nbl::asset #endif \ No newline at end of file diff --git a/include/nbl/asset/interchange/IGeometryLoader.h b/include/nbl/asset/interchange/IGeometryLoader.h new file mode 100644 index 0000000000..b6ae764cfc --- /dev/null +++ b/include/nbl/asset/interchange/IGeometryLoader.h @@ -0,0 +1,31 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_GEOMETRY_LOADER_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_LOADER_H_INCLUDED_ + + +#include "nbl/core/declarations.h" + +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/asset/interchange/IAssetLoader.h" +#include "nbl/asset/interchange/IImageAssetHandlerBase.h" + + +namespace nbl::asset +{ + +class IGeometryLoader : public IAssetLoader +{ + public: + virtual inline uint64_t getSupportedAssetTypesBitfield() const override {return IAsset::ET_GEOMETRY;} + + protected: + inline IGeometryLoader() {} + + private: +}; + +} + +#endif diff --git a/include/nbl/asset/interchange/IGeometryWriter.h b/include/nbl/asset/interchange/IGeometryWriter.h new file mode 100644 index 0000000000..d7e7ab964a --- /dev/null +++ b/include/nbl/asset/interchange/IGeometryWriter.h @@ -0,0 +1,31 @@ +// Copyright (C) 2025-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_GEOMETRY_WRITER_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_WRITER_H_INCLUDED_ + + +#include "nbl/core/declarations.h" + +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/asset/interchange/IAssetWriter.h" + + +namespace nbl::asset +{ + +class IGeometryWriter : public IAssetWriter +{ + public: + virtual inline uint64_t getSupportedAssetTypesBitfield() const override {return IAsset::ET_GEOMETRY;} + + protected: + IGeometryWriter() {} + virtual ~IGeometryWriter() = 0; + + private: +}; + +} + +#endif diff --git a/include/nbl/asset/interchange/IImageWriter.h b/include/nbl/asset/interchange/IImageWriter.h index 75f08febc5..8ea18095fe 100644 --- a/include/nbl/asset/interchange/IImageWriter.h +++ b/include/nbl/asset/interchange/IImageWriter.h @@ -1,9 +1,9 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_IMAGE_WRITER_H_INCLUDED_ +#define _NBL_ASSET_I_IMAGE_WRITER_H_INCLUDED_ -#ifndef __NBL_ASSET_I_IMAGE_WRITER_H_INCLUDED__ -#define __NBL_ASSET_I_IMAGE_WRITER_H_INCLUDED__ #include "nbl/core/declarations.h" @@ -14,9 +14,8 @@ #include "nbl/asset/filters/CFlattenRegionsImageFilter.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { class IImageWriter : public IAssetWriter, public IImageAssetHandlerBase @@ -32,6 +31,4 @@ class IImageWriter : public IAssetWriter, public IImageAssetHandlerBase }; } -} - #endif diff --git a/include/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h b/include/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h deleted file mode 100644 index 0cecb972df..0000000000 --- a/include/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_LOADER_H_INCLUDED__ -#define __NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_LOADER_H_INCLUDED__ - -#include "nbl/core/declarations.h" - -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" -#include "nbl/asset/interchange/IAssetLoader.h" - -namespace nbl::asset -{ - -class IRenderpassIndependentPipelineLoader : public IAssetLoader -{ - public: - virtual void initialize() override; - - protected: - IAssetManager* m_assetMgr; - core::smart_refctd_dynamic_array m_basicViewParamsSemantics; - - inline IRenderpassIndependentPipelineLoader(IAssetManager* _am) : m_assetMgr(_am) {} - virtual ~IRenderpassIndependentPipelineLoader() = 0; - - // samplers - static inline std::string genSamplerCacheKey(const asset::ICPUSampler::SParams& params) - { - // TODO: Change the HASH, ACTUALLY BUILD IT OUT OF ALL THE PARAMETERS, THERE CANNOT BE ANY COLLISIONS! - const std::size_t hash = std::hash{}(std::string_view(reinterpret_cast(¶ms), sizeof(params))); - return "nbl/builtin/sampler/" + std::to_string(hash); - } - static inline core::smart_refctd_ptr getSampler(asset::ICPUSampler::SParams&& params, const IAssetLoader::SAssetLoadContext& context, IAssetLoaderOverride* _override) - { - const auto cacheKey = genSamplerCacheKey(params); - - auto found = _override->findDefaultAsset(cacheKey,context,0u).first; // cached builtins have level 0 - if (found) - return found; - - auto sampler = core::make_smart_refctd_ptr(std::move(params)); - SAssetBundle samplerBundle = SAssetBundle(nullptr,{sampler}); - _override->insertAssetIntoCache(samplerBundle,cacheKey,context,0u); // cached builtins have level 0 - return sampler; - } - - private: -}; - -} - -#endif diff --git a/include/nbl/asset/metadata/CMTLMetadata.h b/include/nbl/asset/metadata/CMTLMetadata.h index dddc0e69d8..08279edba6 100644 --- a/include/nbl/asset/metadata/CMTLMetadata.h +++ b/include/nbl/asset/metadata/CMTLMetadata.h @@ -1,20 +1,20 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_MTL_METADATA_H_INCLUDED_ +#define _NBL_ASSET_C_MTL_METADATA_H_INCLUDED_ -#ifndef __NBL_ASSET_C_MTL_METADATA_H_INCLUDED__ -#define __NBL_ASSET_C_MTL_METADATA_H_INCLUDED__ #include "nbl/asset/metadata/IAssetMetadata.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { class CMTLMetadata final : public IAssetMetadata { public: +#if 0 class CRenderpassIndependentPipeline : public IRenderpassIndependentPipelineMetadata { friend class COBJMetadata; @@ -88,7 +88,6 @@ class CMTLMetadata final : public IAssetMetadata //VS Intellisense shows error here because it think vectorSIMDf is 32 bytes, but it just Intellisense - it'll build anyway static_assert(sizeof(SMaterialParameters) == 128ull, "Something went wrong"); - CRenderpassIndependentPipeline() : IRenderpassIndependentPipelineMetadata(), m_descriptorSet3(), m_materialParams(), m_name(), m_hash(0xdeadbeefu) {} CRenderpassIndependentPipeline(CRenderpassIndependentPipeline&& other) { @@ -124,45 +123,13 @@ class CMTLMetadata final : public IAssetMetadata //for permutations of pipeline representing same material but with different factors impossible to know from MTL file (like whether submesh using the material contains UVs) uint32_t m_hash; }; +#endif - CMTLMetadata(uint32_t pplnCount, core::smart_refctd_dynamic_array&& _semanticStorage) : - IAssetMetadata(), m_metaStorage(createContainer(pplnCount)), m_semanticStorage(std::move(_semanticStorage)) - { - } + CMTLMetadata() : IAssetMetadata() {} _NBL_STATIC_INLINE_CONSTEXPR const char* LoaderName = "CGraphicsPipelineLoaderMTL"; const char* getLoaderName() const override { return LoaderName; } - - //! - inline const CRenderpassIndependentPipeline* getAssetSpecificMetadata(const ICPURenderpassIndependentPipeline* asset) const - { - const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); - } - - private: - meta_container_t m_metaStorage; - core::smart_refctd_dynamic_array m_semanticStorage; - - friend class CGraphicsPipelineLoaderMTL; - inline void placeMeta( - uint32_t offset, const ICPURenderpassIndependentPipeline* ppln, - core::smart_refctd_ptr&& _descriptorSet3, - const CRenderpassIndependentPipeline::SMaterialParameters& _materialParams, - std::string&& _name, uint32_t _hash) - { - auto& meta = m_metaStorage->operator[](offset); - meta = CRenderpassIndependentPipeline( - core::SRange(m_semanticStorage->begin(),m_semanticStorage->end()), - std::move(_descriptorSet3),_materialParams, - std::move(_name),_hash - ); - - IAssetMetadata::insertAssetSpecificMetadata(ppln, &meta); - } }; } -} - #endif diff --git a/include/nbl/asset/metadata/COBJMetadata.h b/include/nbl/asset/metadata/COBJMetadata.h deleted file mode 100644 index 5eea77c473..0000000000 --- a/include/nbl/asset/metadata/COBJMetadata.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_OBJ_METADATA_H_INCLUDED__ -#define __NBL_ASSET_C_OBJ_METADATA_H_INCLUDED__ - -#include "nbl/asset/metadata/IAssetMetadata.h" -#include "nbl/asset/metadata/CMTLMetadata.h" - -namespace nbl -{ -namespace asset -{ - -class COBJMetadata final : public IAssetMetadata -{ - public: - using CRenderpassIndependentPipeline = typename CMTLMetadata::CRenderpassIndependentPipeline; - COBJMetadata(uint32_t pplnCount) : IAssetMetadata(), m_metaStorage(createContainer(pplnCount)) - { - } - - _NBL_STATIC_INLINE_CONSTEXPR const char* LoaderName = "CGraphicsPipelineLoaderMTL"; - const char* getLoaderName() const override { return LoaderName; } - - //! - inline const CRenderpassIndependentPipeline* getAssetSpecificMetadata(const ICPURenderpassIndependentPipeline* asset) const - { - const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); - } - - private: - meta_container_t m_metaStorage; - - friend class COBJMeshFileLoader; - inline void placeMeta(uint32_t offset, const ICPURenderpassIndependentPipeline* ppln, const CRenderpassIndependentPipeline& _meta) - { - auto& meta = m_metaStorage->operator[](offset); - meta.m_inputSemantics = _meta.m_inputSemantics; - meta.m_descriptorSet3 = _meta.m_descriptorSet3; - meta.m_materialParams = _meta.m_materialParams; - meta.m_name = _meta.m_name; - meta.m_hash = _meta.m_hash; - - IAssetMetadata::insertAssetSpecificMetadata(ppln,&meta); - } -}; - -} -} - -#endif diff --git a/include/nbl/asset/metadata/CPLYMetadata.h b/include/nbl/asset/metadata/CPLYMetadata.h index 618d21c255..39ad07561a 100644 --- a/include/nbl/asset/metadata/CPLYMetadata.h +++ b/include/nbl/asset/metadata/CPLYMetadata.h @@ -1,64 +1,24 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_PLY_METADATA_H_INCLUDED_ +#define _NBL_ASSET_C_PLY_METADATA_H_INCLUDED_ -#ifndef __NBL_ASSET_C_PLY_METADATA_H_INCLUDED__ -#define __NBL_ASSET_C_PLY_METADATA_H_INCLUDED__ #include "nbl/asset/metadata/IAssetMetadata.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { class CPLYMetadata final : public IAssetMetadata { - public: - class CRenderpassIndependentPipeline : public IRenderpassIndependentPipelineMetadata - { - public: - CRenderpassIndependentPipeline(CRenderpassIndependentPipeline&& _other) : CRenderpassIndependentPipeline() - { - CRenderpassIndependentPipeline::operator=(std::move(_other)); - } - template - CRenderpassIndependentPipeline(Args&&... args) : IRenderpassIndependentPipelineMetadata(std::forward(args)...) {} - - inline CRenderpassIndependentPipeline& operator=(CRenderpassIndependentPipeline&& other) - { - IRenderpassIndependentPipelineMetadata::operator=(std::move(other)); - return *this; - } - }; - - CPLYMetadata(uint32_t pplnCount, core::smart_refctd_dynamic_array&& _semanticStorage) : - IAssetMetadata(), m_metaStorage(createContainer(pplnCount)), m_semanticStorage(std::move(_semanticStorage)) {} + public: + CPLYMetadata() : IAssetMetadata() {} _NBL_STATIC_INLINE_CONSTEXPR const char* LoaderName = "CPLYMeshFileLoader"; const char* getLoaderName() const override { return LoaderName; } - - //! - inline const CRenderpassIndependentPipeline* getAssetSpecificMetadata(const ICPURenderpassIndependentPipeline* asset) const - { - const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); - } - - private: - meta_container_t m_metaStorage; - core::smart_refctd_dynamic_array m_semanticStorage; - - friend class CPLYMeshFileLoader; - inline void placeMeta(uint32_t offset, const ICPURenderpassIndependentPipeline* ppln) - { - auto& meta = m_metaStorage->operator[](offset); - meta.m_inputSemantics = {m_semanticStorage->begin(),m_semanticStorage->end()}; - IAssetMetadata::insertAssetSpecificMetadata(ppln,&meta); - } }; } -} - #endif \ No newline at end of file diff --git a/include/nbl/asset/metadata/CSTLMetadata.h b/include/nbl/asset/metadata/CSTLMetadata.h index 0dc9a65f69..d295b10381 100644 --- a/include/nbl/asset/metadata/CSTLMetadata.h +++ b/include/nbl/asset/metadata/CSTLMetadata.h @@ -1,20 +1,18 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_STL_METADATA_H_INCLUDED__ -#define __NBL_ASSET_C_STL_METADATA_H_INCLUDED__ +#ifndef _NBL_ASSET_C_STL_METADATA_H_INCLUDED_ +#define _NBL_ASSET_C_STL_METADATA_H_INCLUDED_ #include "nbl/asset/metadata/IAssetMetadata.h" -namespace nbl -{ -namespace asset +namespace nbl::asset { class CSTLMetadata final : public IAssetMetadata { public: +#if 0 class CRenderpassIndependentPipeline : public IRenderpassIndependentPipelineMetadata { public: @@ -36,37 +34,15 @@ class CSTLMetadata final : public IAssetMetadata return IRenderpassIndependentPipelineMetadata::operator!=(other); } }; - - CSTLMetadata(uint32_t pplnCount, core::smart_refctd_dynamic_array&& _semanticStorage) : - IAssetMetadata(), m_metaStorage(createContainer(pplnCount)), m_semanticStorage(std::move(_semanticStorage)) - { - } +#endif + CSTLMetadata() : IAssetMetadata() {} _NBL_STATIC_INLINE_CONSTEXPR const char* LoaderName = "CSTLMeshFileLoader"; const char* getLoaderName() const override { return LoaderName; } - //! - inline const CRenderpassIndependentPipeline* getAssetSpecificMetadata(const ICPURenderpassIndependentPipeline* asset) const - { - const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); - } - private: - meta_container_t m_metaStorage; - core::smart_refctd_dynamic_array m_semanticStorage; - friend class CSTLMeshFileLoader; - inline void placeMeta(uint32_t offset, const ICPURenderpassIndependentPipeline* ppln) - { - auto& meta = m_metaStorage->operator[](offset); - meta = CRenderpassIndependentPipeline(core::SRange(m_semanticStorage->begin(),m_semanticStorage->end())); - - IAssetMetadata::insertAssetSpecificMetadata(ppln,&meta); - } }; } -} - #endif \ No newline at end of file diff --git a/include/nbl/asset/metadata/IAssetMetadata.h b/include/nbl/asset/metadata/IAssetMetadata.h index 28a65cf8be..a6373a5b14 100644 --- a/include/nbl/asset/metadata/IAssetMetadata.h +++ b/include/nbl/asset/metadata/IAssetMetadata.h @@ -9,40 +9,27 @@ #include "nbl/asset/metadata/IImageMetadata.h" #include "nbl/asset/metadata/IImageViewMetadata.h" -#include "nbl/asset/metadata/IRenderpassIndependentPipelineMetadata.h" -#include "nbl/asset/metadata/IMeshMetadata.h" - namespace nbl::asset { -namespace impl{ - class IAssetMetadata_base : public core::IReferenceCounted{ +namespace impl +{ +class IAssetMetadata_base : public core::IReferenceCounted +{ protected: template struct asset_metadata; +}; - }; - - template<> - struct IAssetMetadata_base::asset_metadata - { - using type = IImageMetadata; - }; - template<> - struct IAssetMetadata_base::asset_metadata - { - using type = IImageViewMetadata; - }; - template<> - struct IAssetMetadata_base::asset_metadata - { - using type = IRenderpassIndependentPipelineMetadata; - }; - template<> - struct IAssetMetadata_base::asset_metadata - { - using type = IMeshMetadata; - }; - +template<> +struct IAssetMetadata_base::asset_metadata +{ + using type = IImageMetadata; +}; +template<> +struct IAssetMetadata_base::asset_metadata +{ + using type = IImageViewMetadata; +}; } @@ -83,9 +70,7 @@ class IAssetMetadata : public impl::IAssetMetadata_base std::tuple< asset_metadata_map_t, - asset_metadata_map_t, - asset_metadata_map_t, - asset_metadata_map_t + asset_metadata_map_t > m_metaMaps; diff --git a/include/nbl/asset/metadata/IMeshMetadata.h b/include/nbl/asset/metadata/IMeshMetadata.h deleted file mode 100644 index 5ce3c12980..0000000000 --- a/include/nbl/asset/metadata/IMeshMetadata.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_MESH_METADATA_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_METADATA_H_INCLUDED__ - -#include "nbl/asset/ICPUMesh.h" - -namespace nbl -{ -namespace asset -{ - -//! -class IMeshMetadata : public core::Interface -{ - public: - struct SInstance - { - core::matrix3x4SIMD worldTform; - }; - core::SRange m_instances; - - protected: - IMeshMetadata() : m_instances(nullptr,nullptr) {} - IMeshMetadata(core::SRange&& _instances) : m_instances(std::move(_instances)) {} - virtual ~IMeshMetadata() = default; - - inline IMeshMetadata& operator=(IMeshMetadata&& other) - { - m_instances = other.m_instances; - return *this; - } -}; - -} -} - -#endif diff --git a/include/nbl/asset/metadata/IRenderpassIndependentPipelineMetadata.h b/include/nbl/asset/metadata/IRenderpassIndependentPipelineMetadata.h deleted file mode 100644 index 416c04823b..0000000000 --- a/include/nbl/asset/metadata/IRenderpassIndependentPipelineMetadata.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_METADATA_H_INCLUDED__ -#define __NBL_ASSET_I_RENDERPASS_INDEPENDENT_PIPELINE_METADATA_H_INCLUDED__ - -/* -#include "nbl/asset/ICPUDescriptorSetLayout.h" -#include "nbl/asset/ICPUSpecializedShader.h" -*/ -#include "nbl/asset/ICPUImageView.h" -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" -#include "nbl/asset/IDescriptor.h" -#include "nbl/core/util/to_underlying.h" - -#include - -//#include "nbl/asset/utils/IBuiltinIncludeLoader.h" - -//#include "nbl/asset/asset_utils.h" - -namespace nbl -{ -namespace asset -{ - -//! A class to derive loader-specific pipeline metadata objects from -/** - Pipelines may sometimes require external inputs from outside of the resourced they were built with, for total flexibility - we cannot standardise "conventions" of shader inputs like in game engines or old-style frameworks. - - But we can provide useful metadata from the loader. -*/ -class IRenderpassIndependentPipelineMetadata : public core::Interface -{ - public: - //! A common struct to unify the metadata declarations. - /** - When a pipeline or meshbuffer asset require some inputs to work correctly, - they can put this info in the metadata in a custom way for every loader. - - Most meshbuffers or graphics pipelines will need to know about the model's world view projection matrix, - as well as the inverse transpose of the world matrix, and the camera world position in case of lighting. - - However more advanced loaders such as glTF may want to let us know that there is a texture being used as - an environment map, and with this knowledge we could know that we could, for example change it or provide it - to match the object with the scene. - - Remember that we always have the shader introspector which can give us the information about all the descriptors - that a shader uses, but it won't give us the semantics, or in simple english, the meaning why they are being used. - - (@see ICPURenderpassIndependentPipeilne and @see ICPUComputePipeline) - */ - struct ShaderInput - { - struct DescriptorCommon - { - uint32_t set; - uint32_t binding; - - auto operator<=>(const DescriptorCommon&) const = default; - }; - - struct CombinedImageSampler : DescriptorCommon - { - IImageView::E_TYPE viewType; - // TODO: some info about format class - - auto operator<=>(const CombinedImageSampler&) const = default; - }; - struct StorageImage : DescriptorCommon - { - E_FORMAT format; - - auto operator<=>(const StorageImage&) const = default; - }; - struct TexelBuffer : DescriptorCommon - { - // relative to the start of the IBufferView - uint32_t relByteoffset; - // TODO: some info about format class - - auto operator<=>(const TexelBuffer&) const = default; - }; - struct StorageTexelBuffer : DescriptorCommon - { - // relative to the start of the IBufferView - uint32_t relByteoffset; - E_FORMAT format; - - auto operator<=>(const StorageTexelBuffer&) const = default; - }; - struct Buffer : DescriptorCommon - { - // relative to the offset of the descriptor when bound (true byteoffset = static descriptor-set defined + dynamic [if enabled] + this value) - uint32_t relByteoffset; - uint32_t bytesize; - - auto operator<=>(const Buffer&) const = default; - }; - struct PushConstant - { - uint32_t byteOffset; - - auto operator<=>(const PushConstant&) const = default; - }; - enum class E_TYPE: uint8_t - { - ET_SAMPLER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_SAMPLER), - ET_COMBINED_IMAGE_SAMPLER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER), - ET_SAMPLED_IMAGE = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_SAMPLED_IMAGE), - ET_STORAGE_IMAGE = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_STORAGE_IMAGE), - ET_UNIFORM_TEXEL_BUFFER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER), - ET_STORAGE_TEXEL_BUFFER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_STORAGE_TEXEL_BUFFER), - ET_UNIFORM_BUFFER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_UNIFORM_BUFFER), - ET_STORAGE_BUFFER = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_STORAGE_BUFFER), - ET_INPUT_ATTACHMENT = nbl::core::to_underlying(IDescriptor::E_TYPE::ET_INPUT_ATTACHMENT), - ET_PUSH_CONSTANT = 11 - }; - E_TYPE type; - IShader::E_SHADER_STAGE shaderAccessFlags; - union - { - CombinedImageSampler combinedImageSampler; - StorageImage storageImage; - TexelBuffer texelBuffer; - StorageTexelBuffer storageTexelBuffer; - Buffer uniformBufferObject; - Buffer storageBufferObject; - PushConstant pushConstant; - }; - - inline bool operator!=(const ShaderInput& other) const - { - return !std::memcmp(this, &other, sizeof(other)); - } - }; - - //! A non exhaustive list of commonly used shader input semantics - enum E_COMMON_SHADER_INPUT - { - //! core::matrix4SIMD giving the total projection onto the screen from model-space coordinates - ECSI_WORLD_VIEW_PROJ, - //! core::matrix4SIMD giving the mapping from view-space into the pre-divide NDC space - ECSI_PROJ, - //! core::matrix3x4SIMD giving the view-space transformation from model-space coordinates - ECSI_WORLD_VIEW, - //! core::matrix3x4SIMD giving the view-space transformation from world-space - ECSI_VIEW, - //! core::matrix3x4SIMD giving the world-space transformation from model-space (last column is object world-space-position) - ECSI_WORLD, - //! core::matrix4SIMD giving the total projection to model-space coordinates from screen-space - ECSI_WORLD_VIEW_PROJ_INVERSE, - //! core::matrix4SIMD giving the mapping from the pre-divide NDC space into view-space - ECSI_PROJ_INVERSE, - //! core::matrix3x4SIMD giving the model-space transformation from view-space coordinates - ECSI_WORLD_VIEW_INVERSE, - //! core::matrix3x4SIMD giving the world-space transformation from view-space (last column is camera world-space-position) - ECSI_VIEW_INVERSE, - //! core::matrix3x4SIMD giving the model-space transformation from world-space - ECSI_WORLD_INVERSE, - //! transpose of core::matrix4SIMD giving the total projection to model-space coordinates from screen-space - ECSI_WORLD_VIEW_PROJ_INVERSE_TRANSPOSE, - //! transpose of core::matrix4SIMD giving the mapping from the pre-divide NDC space into view-space - ECSI_PROJ_INVERSE_TRANSPOSE, - //! transpose of core::matrix3x4SIMD giving the model-space transformation from view-space coordinates (upper 3x3 matrix can be used instead of `gl_NormalMatrix`) - ECSI_WORLD_VIEW_INVERSE_TRANSPOSE, - //! transpose of core::matrix3x4SIMD giving the world-space transformation from view-space (last row is camera world-space-position) - ECSI_VIEW_INVERSE_TRANSPOSE, - //! transpose of core::matrix3x4SIMD giving the model-space transformation from world-space (upper 3x3 matrix can transform model space normals to world space) - ECSI_WORLD_INVERSE_TRANSPOSE, - - //! a simple non-filtered environment map as a cubemap - ECSI_ENVIRONMENT_CUBEMAP, - - //! For internal - ECSI_COUNT - }; - //! Tie the semantics to inputs - struct ShaderInputSemantic - { - E_COMMON_SHADER_INPUT type; - ShaderInput descriptorSection; - - inline bool operator!=(const ShaderInputSemantic& other) const - { - return type != other.type || descriptorSection != other.descriptorSection; - } - }; - core::SRange m_inputSemantics; - - inline bool operator!=(const IRenderpassIndependentPipelineMetadata& other) const - { - if (m_inputSemantics.empty()) - return false; - - bool status = false; - for (size_t i = 0; i < m_inputSemantics.size(); ++i) - status = m_inputSemantics.begin()[i] != other.m_inputSemantics.begin()[i]; - - return status; - } - - protected: - IRenderpassIndependentPipelineMetadata() : m_inputSemantics(nullptr,nullptr) {} - IRenderpassIndependentPipelineMetadata(core::SRange&& _inputSemantics) : m_inputSemantics(std::move(_inputSemantics)) {} - virtual ~IRenderpassIndependentPipelineMetadata() = default; - - //! - inline IRenderpassIndependentPipelineMetadata& operator=(IRenderpassIndependentPipelineMetadata&& other) - { - m_inputSemantics = std::move(other.m_inputSemantics); - return *this; - } -}; - - -} -} - -#endif diff --git a/include/nbl/asset/utils/CCPUMeshPackerV1.h b/include/nbl/asset/utils/CCPUMeshPackerV1.h deleted file mode 100644 index 23b2da563a..0000000000 --- a/include/nbl/asset/utils/CCPUMeshPackerV1.h +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_CPU_MESH_PACKER_H_INCLUDED__ -#define __NBL_ASSET_C_CPU_MESH_PACKER_H_INCLUDED__ - -#include -#include -#include - -namespace nbl -{ -namespace asset -{ - -#if 0 // REWRITE -template -class CCPUMeshPackerV1 final : public IMeshPacker -{ - using base_t = IMeshPacker; - using Triangle = typename base_t::Triangle; - using TriangleBatches = typename base_t::TriangleBatches; - using IdxBufferParams = typename base_t::IdxBufferParams; - -public: - struct AllocationParams : IMeshPackerBase::AllocationParamsCommon - { - // Maximum byte size of per instance vertex data allocation - size_t perInstanceVertexBuffSupportedByteSize = 33554432ull; /* 32MB*/ - - // Minimum bytes of per instance vertex data allocated per allocation - size_t perInstanceVertexBufferMinAllocByteSize = 32ull; - }; - - struct ReservedAllocationMeshBuffers : IMeshPackerBase::ReservedAllocationMeshBuffersBase - { - uint32_t instanceAllocationOffset; - uint32_t instanceAllocationReservedSize; - uint32_t vertexAllocationOffset; - uint32_t vertexAllocationReservedSize; - }; - - struct PackerDataStore : base_t::template PackerDataStoreCommon - { - SBufferBinding vertexBufferBindings[SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT] = {}; - SBufferBinding indexBuffer; - - SVertexInputParams vertexInputParams; - }; - - template - struct MeshPackerConfigParams - { - SVertexInputParams vertexInputParams; - core::SRange belongingMeshes; // pointers to sections of `sortedMeshBuffersOut` - }; - -public: - CCPUMeshPackerV1(const SVertexInputParams& preDefinedLayout, const AllocationParams& allocParams, uint16_t minTriangleCountPerMDIData = 256u, uint16_t maxTriangleCountPerMDIData = 1024u); - - ~CCPUMeshPackerV1() - { - if(isInstancingEnabled) - _NBL_ALIGNED_FREE(m_perInsVtxBuffAlctrResSpc); - } - - template - ReservedAllocationMeshBuffers alloc(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd); - - void free(const ReservedAllocationMeshBuffers& ramb) - { - if (ramb.indexAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_idxBuffAlctr.free_addr(ramb.indexAllocationOffset, ramb.indexAllocationReservedCnt); - - if (ramb.mdiAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_MDIDataAlctr.free_addr(ramb.mdiAllocationOffset, ramb.mdiAllocationReservedCnt); - - if (ramb.vertexAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_vtxBuffAlctr.free_addr(ramb.vertexAllocationOffset, ramb.vertexAllocationReservedSize); - - if (ramb.instanceAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_vtxBuffAlctr.free_addr(ramb.instanceAllocationOffset, ramb.instanceAllocationReservedSize); - } - - //needs to be called before first `commit` - void instantiateDataStorage(); - - template - IMeshPackerBase::PackedMeshBufferData commit(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd, ReservedAllocationMeshBuffers& ramb, core::aabbox3df* aabbs); - - inline PackerDataStore& getPackerDataStore() { return m_output; }; - - // returns number of distinct mesh packers needed to pack the meshes and a sorted list of meshes by the meshpacker ID they should be packed into, as well as the parameters for the packers - // `packerParamsOut` should be big enough to fit `std::distance(begin,end)` entries, the return value will tell you how many were actually written - template - static uint32_t getPackerCreationParamsFromMeshBufferRange(const Iterator begin, const Iterator end, Iterator sortedMeshBuffersOut, - MeshPackerConfigParams* packerParamsOut); - - //! shrinks byte size of all output buffers, so they are large enough to fit currently allocated contents. Call this function before `instantiateDataStorage` - virtual void shrinkOutputBuffersSize() override - { - base_t::shrinkOutputBuffersSize(); - - uint32_t perInsBuffNewSize = m_perInsVtxBuffAlctr.safe_shrink_size(0u, base_t::alctrTraits::max_alignment(m_perInsVtxBuffAlctr)); - - if (isInstancingEnabled) - { - const void* oldReserved = base_t::alctrTraits::getReservedSpacePtr(m_perInsVtxBuffAlctr); - m_perInsVtxBuffAlctr = core::GeneralpurposeAddressAllocator(perInsBuffNewSize, std::move(m_perInsVtxBuffAlctr), _NBL_ALIGNED_MALLOC(base_t::alctrTraits::reserved_size(perInsBuffNewSize, m_perInsVtxBuffAlctr), _NBL_SIMD_ALIGNMENT)); - _NBL_ALIGNED_FREE(const_cast(oldReserved)); - } - } - -private: - static bool cmpVtxInputParams(const SVertexInputParams& lhs, const SVertexInputParams& rhs); - -private: - PackerDataStore m_output; - - uint32_t m_vtxSize; - uint32_t m_perInsVtxSize; - - bool isInstancingEnabled; - void* m_perInsVtxBuffAlctrResSpc; - core::GeneralpurposeAddressAllocator m_perInsVtxBuffAlctr; - - _NBL_STATIC_INLINE_CONSTEXPR ReservedAllocationMeshBuffers invalidReservedAllocationMeshBuffers{ base_t::INVALID_ADDRESS, 0, 0, 0, 0, 0, 0, 0 }; - -}; - -template -CCPUMeshPackerV1::CCPUMeshPackerV1(const SVertexInputParams& preDefinedLayout, const AllocationParams& allocParams, uint16_t minTriangleCountPerMDIData, uint16_t maxTriangleCountPerMDIData) - :IMeshPacker(minTriangleCountPerMDIData, maxTriangleCountPerMDIData), - m_perInsVtxBuffAlctrResSpc(nullptr) - -{ - m_output.vertexInputParams.enabledAttribFlags = preDefinedLayout.enabledAttribFlags; - m_output.vertexInputParams.enabledBindingFlags = preDefinedLayout.enabledAttribFlags; - memcpy(m_output.vertexInputParams.attributes, preDefinedLayout.attributes, sizeof(m_output.vertexInputParams.attributes)); - - //1 attrib enabled == 1 binding - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (m_output.vertexInputParams.enabledAttribFlags & attrBit) - { - m_output.vertexInputParams.attributes[location].binding = location; - m_output.vertexInputParams.attributes[location].relativeOffset = 0u; - m_output.vertexInputParams.bindings[location].stride = getTexelOrBlockBytesize(static_cast(m_output.vertexInputParams.attributes[location].format)); - m_output.vertexInputParams.bindings[location].inputRate = preDefinedLayout.bindings[preDefinedLayout.attributes[location].binding].inputRate; - } - } - - m_vtxSize = base_t::calcVertexSize(preDefinedLayout, E_VERTEX_INPUT_RATE::EVIR_PER_VERTEX); - - m_perInsVtxSize = base_t::calcVertexSize(preDefinedLayout, E_VERTEX_INPUT_RATE::EVIR_PER_INSTANCE); - if (m_perInsVtxSize) - { - isInstancingEnabled = true; - m_perInsVtxBuffAlctrResSpc = _NBL_ALIGNED_MALLOC(core::GeneralpurposeAddressAllocator::reserved_size(alignof(std::max_align_t), allocParams.perInstanceVertexBuffSupportedByteSize / m_perInsVtxSize, allocParams.perInstanceVertexBufferMinAllocByteSize), _NBL_SIMD_ALIGNMENT); - assert(m_perInsVtxBuffAlctrResSpc != nullptr); - m_perInsVtxBuffAlctr = core::GeneralpurposeAddressAllocator(m_perInsVtxBuffAlctrResSpc, 0u, 0u, alignof(std::max_align_t), allocParams.perInstanceVertexBuffSupportedByteSize / m_perInsVtxSize, allocParams.perInstanceVertexBufferMinAllocByteSize); - } - else - { - isInstancingEnabled = false; - } - - base_t::initializeCommonAllocators( - { - allocParams.indexBuffSupportedCnt, - m_vtxSize ? allocParams.vertexBuffSupportedByteSize / m_vtxSize : 0ull, - allocParams.MDIDataBuffSupportedCnt, - allocParams.indexBufferMinAllocCnt, - allocParams.vertexBufferMinAllocByteSize, - allocParams.MDIDataBuffMinAllocCnt - } - ); -} - -template -//`Iterator` may be only an Iterator or pointer to pointer -//allocation should be happening even if processed mesh buffer doesn't have attribute that was declared in pre defined `SVertexInputParams`, if mesh buffer has any attributes that are not declared in pre defined `SVertexInputParams` then these should be always ignored -/* - Requirements for input mesh buffers: - - attributes bound to the same binding must have identical format -*/ -template -typename CCPUMeshPackerV1::ReservedAllocationMeshBuffers CCPUMeshPackerV1::alloc(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) -{ - //validation - for (auto it = mbBegin; it != mbEnd; it++) - { - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(m_output.vertexInputParams.enabledAttribFlags & mbVtxInputParams.enabledAttribFlags & attrBit)) - continue; - - if (mbVtxInputParams.attributes[location].format != m_output.vertexInputParams.attributes[location].format || - mbVtxInputParams.bindings[mbVtxInputParams.attributes[location].binding].inputRate != m_output.vertexInputParams.bindings[location].inputRate) - { - assert(false); - return invalidReservedAllocationMeshBuffers; - } - } - } - - size_t idxCnt = 0u; - size_t vtxCnt = 0u; - size_t perInsVtxCnt = 0u; - for (auto it = mbBegin; it != mbEnd; it++) - { - ICPUMeshBuffer* mb = *it; - idxCnt += base_t::calcIdxCntAfterConversionToTriangleList(mb); - vtxCnt += base_t::calcVertexCountBoundWithBatchDuplication(mb); - perInsVtxCnt += mb->getInstanceCount(); - } - - const uint32_t minIdxCntPerPatch = base_t::m_minTriangleCountPerMDIData * 3; - - uint32_t possibleMDIStructsNeededCnt = 0u; - for (auto it = mbBegin; it != mbEnd; it++) - possibleMDIStructsNeededCnt += ((*it)->getIndexCount() + minIdxCntPerPatch - 1) / minIdxCntPerPatch; - - uint32_t MDIAllocAddr = base_t::INVALID_ADDRESS; - uint32_t idxAllocAddr = base_t::INVALID_ADDRESS; - uint32_t vtxAllocAddr = base_t::INVALID_ADDRESS; - uint32_t perInsVtxAllocAddr = base_t::INVALID_ADDRESS; - - MDIAllocAddr = base_t::m_MDIDataAlctr.alloc_addr(possibleMDIStructsNeededCnt, 1u); - if (MDIAllocAddr == base_t::INVALID_ADDRESS) - { - _NBL_DEBUG_BREAK_IF(true); - return invalidReservedAllocationMeshBuffers; - } - - idxAllocAddr = base_t::m_idxBuffAlctr.alloc_addr(idxCnt, 1u); - if (idxAllocAddr == base_t::INVALID_ADDRESS) - { - _NBL_DEBUG_BREAK_IF(true); - - base_t::m_MDIDataAlctr.free_addr(MDIAllocAddr, possibleMDIStructsNeededCnt); - - return invalidReservedAllocationMeshBuffers; - } - - bool arePerVtxAttribsEnabled = base_t::alctrTraits::get_total_size(base_t::m_vtxBuffAlctr) == 0 ? false : true; - if (arePerVtxAttribsEnabled) - { - vtxAllocAddr = base_t::m_vtxBuffAlctr.alloc_addr(vtxCnt * m_vtxSize, 1u); - if (vtxAllocAddr == base_t::INVALID_ADDRESS) - { - _NBL_DEBUG_BREAK_IF(true); - - base_t::m_MDIDataAlctr.free_addr(MDIAllocAddr, possibleMDIStructsNeededCnt); - base_t::m_idxBuffAlctr.free_addr(idxAllocAddr, idxCnt); - - return invalidReservedAllocationMeshBuffers; - } - } - - if (isInstancingEnabled) - { - perInsVtxAllocAddr = m_perInsVtxBuffAlctr.alloc_addr(perInsVtxCnt * m_perInsVtxSize, 1u); - if (perInsVtxAllocAddr == base_t::INVALID_ADDRESS) - { - _NBL_DEBUG_BREAK_IF(true); - - base_t::m_MDIDataAlctr.free_addr(MDIAllocAddr, possibleMDIStructsNeededCnt); - base_t::m_idxBuffAlctr.free_addr(idxAllocAddr, idxCnt); - base_t::m_vtxBuffAlctr.free_addr(vtxAllocAddr, vtxCnt * m_vtxSize); - - return invalidReservedAllocationMeshBuffers; - } - } - - ReservedAllocationMeshBuffers result; - result.mdiAllocationOffset = MDIAllocAddr; - result.mdiAllocationReservedCnt = possibleMDIStructsNeededCnt; - result.indexAllocationOffset = idxAllocAddr; - result.indexAllocationReservedCnt = idxCnt; - result.instanceAllocationOffset = perInsVtxAllocAddr; - result.instanceAllocationReservedSize = perInsVtxAllocAddr == base_t::INVALID_ADDRESS ? 0u : perInsVtxCnt * m_perInsVtxSize; - result.vertexAllocationOffset = vtxAllocAddr; - result.vertexAllocationReservedSize = vtxAllocAddr == base_t::INVALID_ADDRESS ? 0u : vtxCnt * m_vtxSize; - - return result; -} - -template -void CCPUMeshPackerV1::instantiateDataStorage() -{ - const size_t MDIDataBuffSupportedByteSize = base_t::alctrTraits::get_total_size(base_t::m_MDIDataAlctr) * sizeof(MDIStructType); - const size_t idxBuffSupportedByteSize = base_t::alctrTraits::get_total_size(base_t::m_idxBuffAlctr) * sizeof(uint16_t); - const size_t vtxBuffSupportedByteSize = base_t::alctrTraits::get_total_size(base_t::m_vtxBuffAlctr); - const size_t perInsBuffSupportedByteSize = base_t::alctrTraits::get_total_size(base_t::m_vtxBuffAlctr); - - m_output.MDIDataBuffer = core::make_smart_refctd_ptr(MDIDataBuffSupportedByteSize); - m_output.indexBuffer.buffer = core::make_smart_refctd_ptr(idxBuffSupportedByteSize); - - core::smart_refctd_ptr unifiedVtxBuff = core::make_smart_refctd_ptr(vtxBuffSupportedByteSize); - core::smart_refctd_ptr unifiedInsBuff = core::make_smart_refctd_ptr(perInsBuffSupportedByteSize); - - //divide unified vtx buffers - //proportions: sizeOfAttr1 : sizeOfAttr2 : ... : sizeOfAttrN - std::array attrSizeArray; - - uint32_t vtxBufferOffset = 0u; - const uint32_t maxVtxCnt = m_vtxSize == 0u ? 0u : vtxBuffSupportedByteSize / m_vtxSize; - - uint32_t perInsBuffOffset = 0u; - const uint32_t maxPerInsVtxCnt = m_perInsVtxSize == 0u ? 0u : perInsBuffSupportedByteSize / m_perInsVtxSize; - - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (m_output.vertexInputParams.enabledAttribFlags & attrBit) - { - auto attrib = m_output.vertexInputParams.attributes[location]; - auto binding = m_output.vertexInputParams.bindings[attrib.binding]; - - if (binding.inputRate == EVIR_PER_VERTEX) - { - m_output.vertexBufferBindings[location] = { vtxBufferOffset, unifiedVtxBuff }; - vtxBufferOffset += asset::getTexelOrBlockBytesize(static_cast(attrib.format)) * maxVtxCnt; - } - else if (binding.inputRate == EVIR_PER_INSTANCE) - { - m_output.vertexBufferBindings[location] = { perInsBuffOffset, unifiedInsBuff }; - perInsBuffOffset += asset::getTexelOrBlockBytesize(static_cast(attrib.format)) * maxPerInsVtxCnt; - } - } - } - -} - -template -template -IMeshPackerBase::PackedMeshBufferData CCPUMeshPackerV1::commit(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd, CCPUMeshPackerV1::ReservedAllocationMeshBuffers& ramb, core::aabbox3df* aabbs) -{ - MDIStructType* mdiBuffPtr = static_cast(m_output.MDIDataBuffer->getPointer()) + ramb.mdiAllocationOffset; - uint16_t* indexBuffPtr = static_cast(m_output.indexBuffer.buffer->getPointer()) + ramb.indexAllocationOffset; - size_t verticesAddedToUnifiedBufferCnt = 0ull; - size_t instancesAddedCnt = 0ull; - - uint32_t MDIStructsAddedCnt = 0u; - - size_t batchFirstIdx = ramb.indexAllocationOffset; - size_t batchBaseVtx = ramb.vertexAllocationOffset; - - for (auto it = mbBegin; it != mbEnd; it++) - { - const auto mbPrimitiveType = (*it)->getPipeline()->getPrimitiveAssemblyParams().primitiveType; - - IdxBufferParams idxBufferParams = base_t::createNewIdxBufferParamsForNonTriangleListTopologies(*it); - - TriangleBatches triangleBatches = base_t::constructTriangleBatches(*it, idxBufferParams, aabbs); - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - - const uint32_t batchCnt = triangleBatches.ranges.size() - 1u; - for (uint32_t i = 0u; i < batchCnt; i++) - { - auto batchBegin = triangleBatches.ranges[i]; - auto batchEnd = triangleBatches.ranges[i + 1]; - - const uint32_t triangleInBatchCnt = std::distance(batchBegin, batchEnd); - const uint32_t idxInBatchCnt = 3 * triangleInBatchCnt; - - core::unordered_map usedVertices = base_t::constructNewIndicesFromTriangleBatchAndUpdateUnifiedIndexBuffer(triangleBatches, i, indexBuffPtr); - - //copy deinterleaved vertices into unified vertex buffer - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(m_output.vertexInputParams.enabledAttribFlags & mbVtxInputParams.enabledAttribFlags & attrBit)) - continue; - - SVertexInputAttribParams attrib = m_output.vertexInputParams.attributes[location]; - SBufferBinding& vtxBuffBind = m_output.vertexBufferBindings[location]; - const E_VERTEX_INPUT_RATE inputRate = m_output.vertexInputParams.bindings[attrib.binding].inputRate; - uint8_t* dstAttrPtr = static_cast(vtxBuffBind.buffer->getPointer()) + vtxBuffBind.offset; - const size_t attrSize = asset::getTexelOrBlockBytesize(static_cast(attrib.format)); - - if (inputRate == EVIR_PER_VERTEX) - { - dstAttrPtr += (ramb.vertexAllocationOffset + verticesAddedToUnifiedBufferCnt) * attrSize; - base_t::deinterleaveAndCopyAttribute(*it, location, usedVertices, dstAttrPtr); - } - else if (inputRate == EVIR_PER_INSTANCE) - { - dstAttrPtr += (ramb.instanceAllocationOffset + instancesAddedCnt) * attrSize; - base_t::deinterleaveAndCopyPerInstanceAttribute(*it, location, dstAttrPtr); - } - } - - //construct mdi data - MDIStructType MDIData; - MDIData.count = idxInBatchCnt; - MDIData.instanceCount = isInstancingEnabled ? (*it)->getInstanceCount() : 1u; - MDIData.firstIndex = batchFirstIdx; - MDIData.baseVertex = batchBaseVtx; //possible overflow? - MDIData.baseInstance = isInstancingEnabled ? instancesAddedCnt : 0u; - - *mdiBuffPtr = MDIData; - mdiBuffPtr++; - MDIStructsAddedCnt++; - - batchFirstIdx += idxInBatchCnt; - batchBaseVtx += usedVertices.size(); - - verticesAddedToUnifiedBufferCnt += usedVertices.size(); - } - - instancesAddedCnt += (*it)->getInstanceCount(); - } - - return { ramb.mdiAllocationOffset, MDIStructsAddedCnt }; -} - -template -static bool CCPUMeshPackerV1::cmpVtxInputParams(const SVertexInputParams& lhs, const SVertexInputParams& rhs) -{ - if (lhs.enabledAttribFlags != rhs.enabledAttribFlags) - return false; - - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(attrBit & lhs.enabledAttribFlags)) - continue; - - if (lhs.attributes[location].format != rhs.attributes[location].format || - lhs.bindings[lhs.attributes[location].binding].inputRate != rhs.bindings[rhs.attributes[location].binding].inputRate) - return false; - } - - return true; -} - -template -template -static uint32_t CCPUMeshPackerV1::getPackerCreationParamsFromMeshBufferRange(const Iterator begin, const Iterator end, Iterator sortedMeshBuffersOut, - MeshPackerConfigParams* packerParamsOut) -{ - assert(begin <= end); - if (begin == end) - return 0; - - uint32_t packersNeeded = 1u; - - typename IMeshPackerBase::MeshPackerConfigParams firstInpuParams - { - (*begin)->getPipeline()->getVertexInputParams(), - SRange(sortedMeshBuffersOut, sortedMeshBuffersOut) - }; - memcpy(packerParamsOut, &firstInpuParams, sizeof(SVertexInputParams)); - - //fill array - auto test1 = std::distance(begin, end); - auto* packerParamsOutEnd = packerParamsOut + 1u; - for (Iterator it = begin + 1; it != end; it++) - { - auto& currMeshVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - - bool alreadyInserted = false; - for (auto* packerParamsIt = packerParamsOut; packerParamsIt != packerParamsOutEnd; packerParamsIt++) - { - alreadyInserted = cmpVtxInputParams(packerParamsIt->vertexInputParams, currMeshVtxInputParams); - - if (alreadyInserted) - break; - } - - if (!alreadyInserted) - { - packersNeeded++; - - typename IMeshPackerBase::MeshPackerConfigParams configParams - { - currMeshVtxInputParams, - SRange(sortedMeshBuffersOut, sortedMeshBuffersOut) - }; - memcpy(packerParamsOutEnd, &configParams, sizeof(SVertexInputParams)); - packerParamsOutEnd++; - } - } - - auto getIndexOfArrayElement = [&](const SVertexInputParams& vtxInputParams) -> int32_t - { - int32_t offset = 0u; - for (auto* it = packerParamsOut; it != packerParamsOutEnd; it++, offset++) - { - if (cmpVtxInputParams(vtxInputParams, it->vertexInputParams)) - return offset; - - if (it == packerParamsOut - 1) - return -1; - } - }; - - //sort meshes by SVertexInputParams - const Iterator sortedMeshBuffersOutEnd = sortedMeshBuffersOut + std::distance(begin, end); - - std::copy(begin, end, sortedMeshBuffersOut); - std::sort(sortedMeshBuffersOut, sortedMeshBuffersOutEnd, - [&](const ICPUMeshBuffer* lhs, const ICPUMeshBuffer* rhs) - { - return getIndexOfArrayElement(lhs->getPipeline()->getVertexInputParams()) < getIndexOfArrayElement(rhs->getPipeline()->getVertexInputParams()); - } - ); - - //set ranges - Iterator sortedMeshBuffersIt = sortedMeshBuffersOut; - for (auto* inputParamsIt = packerParamsOut; inputParamsIt != packerParamsOutEnd; inputParamsIt++) - { - Iterator firstMBForThisRange = sortedMeshBuffersIt; - Iterator lastMBForThisRange = sortedMeshBuffersIt; - for (Iterator it = firstMBForThisRange; it != sortedMeshBuffersOutEnd; it++) - { - if (!cmpVtxInputParams(inputParamsIt->vertexInputParams, (*it)->getPipeline()->getVertexInputParams())) - { - lastMBForThisRange = it; - break; - } - } - - if (inputParamsIt == packerParamsOutEnd - 1) - lastMBForThisRange = sortedMeshBuffersOutEnd; - - inputParamsIt->belongingMeshes = SRange(firstMBForThisRange, lastMBForThisRange); - sortedMeshBuffersIt = lastMBForThisRange; - } - - return packersNeeded; -} -#endif -} -} - -#endif diff --git a/include/nbl/asset/utils/CCPUMeshPackerV2.h b/include/nbl/asset/utils/CCPUMeshPackerV2.h deleted file mode 100644 index 83308ab82b..0000000000 --- a/include/nbl/asset/utils/CCPUMeshPackerV2.h +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_CPU_MESH_PACKER_V2_H_INCLUDED__ -#define __NBL_ASSET_C_CPU_MESH_PACKER_V2_H_INCLUDED__ - -#include -#include - -namespace nbl -{ -namespace asset -{ -#if 0 // REWRITE -template -class CCPUMeshPackerV2 final : public IMeshPackerV2 -{ - using base_t = IMeshPackerV2; - using Triangle = typename base_t::Triangle; - using TriangleBatches = typename base_t::TriangleBatches; - using IdxBufferParams = typename base_t::base_t::IdxBufferParams; - - public: - using AllocationParams = IMeshPackerBase::AllocationParamsCommon; - using PackerDataStore = typename base_t::PackerDataStore; - using ReservedAllocationMeshBuffers = typename base_t::ReservedAllocationMeshBuffers; - using AttribAllocParams = typename base_t::AttribAllocParams; - using CombinedDataOffsetTable = typename base_t::CombinedDataOffsetTable; - - public: - CCPUMeshPackerV2(const AllocationParams& allocParams, const IMeshPackerV2Base::SupportedFormatsContainer& formats, uint16_t minTriangleCountPerMDIData = 256u, uint16_t maxTriangleCountPerMDIData = 1024u) - : base_t(allocParams, formats, minTriangleCountPerMDIData, maxTriangleCountPerMDIData) - {} - - void instantiateDataStorage(); - - /** - \return number of mdi structs created for mesh buffer range described by mbBegin .. mbEnd, 0 if commit failed or mbBegin == mbEnd - */ - template - uint32_t commit(IMeshPackerBase::PackedMeshBufferData* pmbdOut, CombinedDataOffsetTable* cdotOut, core::aabbox3df* aabbs, ReservedAllocationMeshBuffers* rambIn, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd); - - inline std::pair getDescriptorSetWritesForUTB( - ICPUDescriptorSet::SWriteDescriptorSet* outWrites, ICPUDescriptorSet::SDescriptorInfo* outInfo, ICPUDescriptorSet* dstSet, - const typename base_t::DSLayoutParamsUTB& params = {} - ) const - { - auto createBufferView = [&](core::smart_refctd_ptr&& buff, E_FORMAT format) -> core::smart_refctd_ptr - { - return core::make_smart_refctd_ptr(std::move(buff),format); - }; - return base_t::getDescriptorSetWritesForUTB(outWrites,outInfo,dstSet,createBufferView,params); - } -}; - -template -void CCPUMeshPackerV2::instantiateDataStorage() -{ - const uint32_t MDIDataBuffByteSize = base_t::m_MDIDataAlctr.get_total_size() * sizeof(MDIStructType); - const uint32_t idxBuffByteSize = base_t::m_idxBuffAlctr.get_total_size() * sizeof(uint16_t); - const uint32_t vtxBuffByteSize = base_t::m_vtxBuffAlctr.get_total_size(); - - base_t::m_packerDataStore.MDIDataBuffer = core::make_smart_refctd_ptr(MDIDataBuffByteSize); - base_t::m_packerDataStore.indexBuffer = core::make_smart_refctd_ptr(idxBuffByteSize); - base_t::m_packerDataStore.vertexBuffer = core::make_smart_refctd_ptr(vtxBuffByteSize); -} - -/* - @param pmbdOut size of this array has to be >= std::distance(mbBegin, mbEnd) - @param cdotOut size of this array has to be >= IMeshPackerV2::calcMDIStructMaxCount(mbBegin, mbEnd) -*/ -template -template -uint32_t CCPUMeshPackerV2::commit(IMeshPackerBase::PackedMeshBufferData* pmbdOut, CombinedDataOffsetTable* cdotOut, core::aabbox3df* aabbs, ReservedAllocationMeshBuffers* rambIn, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) -{ - MDIStructType* mdiBuffPtr = static_cast(base_t::m_packerDataStore.MDIDataBuffer->getPointer()) + rambIn->mdiAllocationOffset; - - size_t i = 0ull; - uint32_t batchCntTotal = 0u; - for (auto it = mbBegin; it != mbEnd; it++) - { - const ReservedAllocationMeshBuffers& ramb = *(rambIn + i); - IMeshPackerBase::PackedMeshBufferData& pmbd = *(pmbdOut + i); - - //this is fucked up.. - //mdiAllocationOffset should be one for all mesh buffers in range defined by mbBegin .. mbEnd, otherwise things get fucked when there are random sizes of batches - //TODO: so modify ReservedAllocationMeshBuffers and free function - //MDIStructType* mdiBuffPtr = static_cast(m_packerDataStore.MDIDataBuffer->getPointer()) + ramb.mdiAllocationOffset; - uint16_t* indexBuffPtr = static_cast(base_t::m_packerDataStore.indexBuffer->getPointer()) + ramb.indexAllocationOffset; - - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - const uint32_t insCnt = (*it)->getInstanceCount(); - - IdxBufferParams idxBufferParams = base_t::createNewIdxBufferParamsForNonTriangleListTopologies(*it); - - TriangleBatches triangleBatches = base_t::constructTriangleBatches(*it, idxBufferParams, aabbs); - - size_t batchFirstIdx = ramb.indexAllocationOffset; - size_t verticesAddedCnt = 0u; - - //TODO: check if mpv1 does redundand copies - std::array perInsAttribFromThisLocationWasCopied; - std::fill(perInsAttribFromThisLocationWasCopied.begin(), perInsAttribFromThisLocationWasCopied.end(), false); - - const uint32_t batchCnt = triangleBatches.ranges.size() - 1u; - for (uint32_t i = 0u; i < batchCnt; i++) - { - auto batchBegin = triangleBatches.ranges[i]; - auto batchEnd = triangleBatches.ranges[i+1]; - const uint32_t triangleInBatchCnt = std::distance(batchBegin,batchEnd); - constexpr uint32_t kIndicesPerTriangle = 3u; - const uint32_t idxInBatchCnt = triangleInBatchCnt*kIndicesPerTriangle; - - core::unordered_map usedVertices = base_t::constructNewIndicesFromTriangleBatchAndUpdateUnifiedIndexBuffer(triangleBatches, i, indexBuffPtr); - - //copy deinterleaved vertices into unified vertex buffer - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(mbVtxInputParams.enabledAttribFlags & attrBit)) - continue; - - if (ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - return 0u; - - const E_FORMAT attribFormat = static_cast(mbVtxInputParams.attributes[location].format); - const uint32_t attribSize = asset::getTexelOrBlockBytesize(attribFormat); - const uint32_t binding = mbVtxInputParams.attributes[location].binding; - const E_VERTEX_INPUT_RATE inputRate = mbVtxInputParams.bindings[binding].inputRate; - - uint8_t* dstAttrPtr = static_cast(base_t::m_packerDataStore.vertexBuffer->getPointer()) + ramb.attribAllocParams[location].offset; - - if (inputRate == EVIR_PER_VERTEX) - { - const uint32_t currBatchOffsetForPerVtxAttribs = verticesAddedCnt * attribSize; - dstAttrPtr += currBatchOffsetForPerVtxAttribs; - base_t::deinterleaveAndCopyAttribute(*it, location, usedVertices, dstAttrPtr); - } - if (inputRate == EVIR_PER_INSTANCE) - { - if (perInsAttribFromThisLocationWasCopied[location] == false) - { - base_t::deinterleaveAndCopyPerInstanceAttribute(*it, location, dstAttrPtr); - perInsAttribFromThisLocationWasCopied[location] = true; - } - } - - auto& utb = base_t::m_virtualAttribConfig.utbs[base_t::VirtualAttribConfig::getUTBArrayTypeFromFormat(attribFormat)]; - auto vtxFormatInfo = utb.find(attribFormat); - if (vtxFormatInfo==utb.end()) - return 0u; - - uint16_t vaArrayElement = vtxFormatInfo->second; - uint32_t vaOffset; - - if (inputRate == EVIR_PER_VERTEX) - vaOffset = ramb.attribAllocParams[location].offset / attribSize + verticesAddedCnt; - if (inputRate == EVIR_PER_INSTANCE) - vaOffset = ramb.attribAllocParams[location].offset / attribSize; - - cdotOut->attribInfo[location] = base_t::VirtualAttribute(vaArrayElement,vaOffset); - - } - - verticesAddedCnt += usedVertices.size(); - cdotOut++; - - //construct mdi data - MDIStructType MDIData; - MDIData.count = idxInBatchCnt; - MDIData.instanceCount = (*it)->getInstanceCount(); - MDIData.firstIndex = batchFirstIdx; - MDIData.baseVertex = 0u; - MDIData.baseInstance = 0u; - - *mdiBuffPtr = MDIData; - mdiBuffPtr++; - - batchFirstIdx += idxInBatchCnt; - } - - pmbd = { rambIn->mdiAllocationOffset+batchCntTotal, static_cast(batchCnt) }; - batchCntTotal += batchCnt; - - i++; - } - - return batchCntTotal; -} -#endif -} -} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/utils/CDirQuantCacheBase.h b/include/nbl/asset/utils/CDirQuantCacheBase.h index 4057e3e4d9..5475f36932 100644 --- a/include/nbl/asset/utils/CDirQuantCacheBase.h +++ b/include/nbl/asset/utils/CDirQuantCacheBase.h @@ -244,7 +244,7 @@ struct CDirQuantCacheBase::value_type template -class CDirQuantCacheBase : public impl::CDirQuantCacheBase +class CDirQuantCacheBase : public virtual core::IReferenceCounted, public impl::CDirQuantCacheBase { public: template @@ -282,7 +282,7 @@ class CDirQuantCacheBase : public impl::CDirQuantCacheBase backup.swap(particularCache); CBufferPhmapInputArchive buffWrap(buffer); - bool loadingSuccess = particularCache.load(buffWrap); + bool loadingSuccess = particularCache.phmap_load(buffWrap); if (!replaceCurrentContents || !loadingSuccess) particularCache.merge(std::move(backup)); @@ -333,7 +333,7 @@ class CDirQuantCacheBase : public impl::CDirQuantCacheBase return false; CBufferPhmapOutputArchive buffWrap(buffer); - return std::get>(cache).dump(buffWrap); + return std::get>(cache).phmap_dump(buffWrap); } //! diff --git a/include/nbl/asset/utils/IGeometryCreator.h b/include/nbl/asset/utils/CGeometryCreator.h similarity index 54% rename from include/nbl/asset/utils/IGeometryCreator.h rename to include/nbl/asset/utils/CGeometryCreator.h index 0fcfefcb5e..87d7a0ef5e 100644 --- a/include/nbl/asset/utils/IGeometryCreator.h +++ b/include/nbl/asset/utils/CGeometryCreator.h @@ -1,45 +1,47 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_GEOMETRY_CREATOR_H_INCLUDED_ +#define _NBL_ASSET_C_GEOMETRY_CREATOR_H_INCLUDED_ -#ifndef __NBL_ASSET_I_GEOMETRY_CREATOR_H_INCLUDED__ -#define __NBL_ASSET_I_GEOMETRY_CREATOR_H_INCLUDED__ #include "nbl/core/declarations.h" -#include "nbl/asset/ICPUMesh.h" -#include "nbl/asset/utils/IMeshManipulator.h" - +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" +// legacy, needs to be removed #include "SColor.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { //! Helper class for creating geometry on the fly. /** You can get an instance of this class through ISceneManager::getGeometryCreator() */ -class IGeometryCreator : public core::IReferenceCounted +class NBL_API2 CGeometryCreator final : public core::IReferenceCounted { - _NBL_INTERFACE_CHILD(IGeometryCreator) {} public: - struct return_type + struct SCreationParams { - SVertexInputParams inputParams; - SPrimitiveAssemblyParams assemblyParams; - SBufferBinding bindings[ICPUMeshBuffer::MAX_ATTR_BUF_BINDING_COUNT]; - SBufferBinding indexBuffer; - E_INDEX_TYPE indexType; - uint32_t indexCount; - core::aabbox3df bbox; + core::smart_refctd_ptr normalCache = nullptr; + core::smart_refctd_ptr quaternionCache = nullptr; }; + inline CGeometryCreator(SCreationParams&& params={}) : m_params(std::move(params)) + { + if (!m_params.normalCache) + m_params.normalCache = core::make_smart_refctd_ptr(); + if (!m_params.quaternionCache) + m_params.quaternionCache = core::make_smart_refctd_ptr(); + } + + // + const SCreationParams& getCreationParams() const {return m_params;} //! Creates a simple cube mesh. /** \param size Dimensions of the cube. \return Generated mesh. */ - virtual return_type createCubeMesh(const core::vector3df& size=core::vector3df(5.f,5.f,5.f)) const =0; + core::smart_refctd_ptr createCube(const hlsl::float32_t3 size={5.f,5.f,5.f}) const; //! Create an arrow mesh, composed of a cylinder and a cone. @@ -56,11 +58,11 @@ class IGeometryCreator : public core::IReferenceCounted \param colorCone color of the cone \return Generated mesh. */ - virtual return_type createArrowMesh(const uint32_t tesselationCylinder = 4, + core::smart_refctd_ptr createArrow(const uint32_t tesselationCylinder = 4, const uint32_t tesselationCone = 8, const float height = 1.f, const float cylinderHeight = 0.6f, const float widthCylinder = 0.05f, const float widthCone = 0.3f, const video::SColor colorCylinder = 0xFFFFFFFF, - const video::SColor colorCone = 0xFFFFFFFF, IMeshManipulator* const meshManipulatorOverride = nullptr) const =0; + const video::SColor colorCone = 0xFFFFFFFF) const; //! Create a sphere mesh. @@ -70,8 +72,8 @@ class IGeometryCreator : public core::IReferenceCounted \param polyCountY Number of quads used for the vertical tiling \return Generated mesh. */ - virtual return_type createSphereMesh(float radius = 5.f, - uint32_t polyCountX = 16, uint32_t polyCountY = 16, IMeshManipulator* const meshManipulatorOverride = nullptr) const =0; + core::smart_refctd_ptr createSphere(float radius = 5.f, + uint32_t polyCountX = 16, uint32_t polyCountY = 16, CQuantNormalCache* const quantNormalCacheOverride=nullptr) const; //! Create a cylinder mesh. /** @@ -83,9 +85,9 @@ class IGeometryCreator : public core::IReferenceCounted \param oblique (to be documented) \return Generated mesh. */ - virtual return_type createCylinderMesh(float radius, float length, + core::smart_refctd_ptr createCylinder(float radius, float length, uint32_t tesselation, - const video::SColor& color=video::SColor(0xffffffff), IMeshManipulator* const meshManipulatorOverride = nullptr) const =0; + const video::SColor& color=video::SColor(0xffffffff), CQuantNormalCache* const quantNormalCacheOverride=nullptr) const; //! Create a cone mesh. /** @@ -97,14 +99,14 @@ class IGeometryCreator : public core::IReferenceCounted \param oblique (to be documented) \return Generated mesh. */ - virtual return_type createConeMesh(float radius, float length, uint32_t tesselation, + core::smart_refctd_ptr createCone(float radius, float length, uint32_t tesselation, const video::SColor& colorTop=video::SColor(0xffffffff), const video::SColor& colorBottom=video::SColor(0xffffffff), - float oblique=0.f, IMeshManipulator* const meshManipulatorOverride = nullptr) const =0; + float oblique=0.f, CQuantNormalCache* const quantNormalCacheOverride=nullptr) const; - virtual return_type createRectangleMesh(const core::vector2df_SIMD& size = core::vector2df_SIMD(0.5f, 0.5f)) const = 0; + core::smart_refctd_ptr createRectangle(const hlsl::float32_t2 size={0.5f,0.5f}) const; - virtual return_type createDiskMesh(float radius, uint32_t tesselation) const = 0; + core::smart_refctd_ptr createDisk(const float radius, const uint32_t tesselation) const; //! Create a icosphere geometry /** @@ -113,12 +115,12 @@ class IGeometryCreator : public core::IReferenceCounted \param smooth Specifies whether vertecies should be built for smooth or flat shading. */ - virtual return_type createIcoSphere(float radius = 1.0f, uint32_t subdivision = 1, bool smooth = false) const = 0; + core::smart_refctd_ptr createIcoSphere(float radius=1.f, uint32_t subdivision=1, bool smooth=false) const; + private: + SCreationParams m_params; }; -} // end namespace asset -} // end namespace nbl - +} // end namespace nbl::asset #endif diff --git a/include/nbl/asset/utils/IMeshManipulator.h b/include/nbl/asset/utils/CPolygonGeometryManipulator.h similarity index 72% rename from include/nbl/asset/utils/IMeshManipulator.h rename to include/nbl/asset/utils/CPolygonGeometryManipulator.h index f84d85c75d..86be563356 100644 --- a/include/nbl/asset/utils/IMeshManipulator.h +++ b/include/nbl/asset/utils/CPolygonGeometryManipulator.h @@ -1,37 +1,107 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_POLYGON_GEOMETRY_MANIPULATOR_H_INCLUDED_ +#define _NBL_ASSET_C_POLYGON_GEOMETRY_MANIPULATOR_H_INCLUDED_ -#ifndef __NBL_ASSET_I_MESH_MANIPULATOR_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_MANIPULATOR_H_INCLUDED__ - -#include -#include #include "nbl/core/declarations.h" -#include "vector3d.h" -#include "aabbox3d.h" - -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/ICPUMesh.h" +#include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/utils/CQuantNormalCache.h" #include "nbl/asset/utils/CQuantQuaternionCache.h" -namespace nbl +namespace nbl::asset { -namespace asset + +// TODO: move to its own header! +class NBL_API2 CGeometryManipulator { + public: + static inline void recomputeContentHash(const IGeometry::SDataView& view) + { + if (!view) + return; + view.src.buffer->setContentHash(view.src.buffer->computeContentHash()); + } -//! An interface for easy manipulation of meshes. -/** Scale, set alpha value, flip surfaces, and so on. This exists for -fixing problems with wrong imported or exported meshes quickly after -loading. It is not intended for doing mesh modifications and/or -animations during runtime. -*/ -class NBL_API2 IMeshManipulator : public virtual core::IReferenceCounted + static inline IGeometryBase::SAABBStorage computeRange(const IGeometry::SDataView& view) + { + if (!view || !view.composed.isFormatted()) + return {}; + auto it = reinterpret_cast(view.src.buffer->getPointer())+view.src.offset; + const auto end = it+view.src.actualSize(); + auto addToAABB = [&](auto& aabb)->void + { + using aabb_t = std::remove_reference_t; + for (auto i=0; i!=view.getElementCount(); i++) + { + typename aabb_t::point_t pt; + view.decodeElement(i,pt); + aabb.addPoint(pt); + } + }; + IGeometryBase::SDataViewBase tmp = {}; + tmp.resetRange(view.composed.rangeFormat); + tmp.visitAABB(addToAABB); + return tmp.encodedDataRange; + } + + static inline void recomputeRange(IGeometry::SDataView& view, const bool deduceRangeFormat=true) + { + if (!view || !view.composed.isFormatted()) + return; + if (deduceRangeFormat) + view.composed.rangeFormat = IGeometryBase::getMatchingAABBFormat(view.composed.format); + view.composed.encodedDataRange = computeRange(view); + } +}; + +//! An interface for easy manipulation of polygon geometries. +class NBL_API2 CPolygonGeometryManipulator { public: + static inline void recomputeContentHashes(ICPUPolygonGeometry* geo) + { + if (!geo) + return; + CGeometryManipulator::recomputeContentHash(geo->getPositionView()); + CGeometryManipulator::recomputeContentHash(geo->getIndexView()); + CGeometryManipulator::recomputeContentHash(geo->getNormalView()); + for (const auto& view : *geo->getJointWeightViews()) + { + CGeometryManipulator::recomputeContentHash(view.indices); + CGeometryManipulator::recomputeContentHash(view.weights); + } + if (auto pView=geo->getJointOBBView(); pView) + CGeometryManipulator::recomputeContentHash(*pView); + for (const auto& view : *geo->getAuxAttributeViews()) + CGeometryManipulator::recomputeContentHash(view); + } + + // + static inline void recomputeRanges(ICPUPolygonGeometry* geo, const bool deduceRangeFormats=true) + { + if (!geo) + return; + auto recomputeRange = [deduceRangeFormats](const IGeometry::SDataView& view)->void + { + CGeometryManipulator::recomputeRange(const_cast::SDataView&>(view),deduceRangeFormats); + }; + recomputeRange(geo->getPositionView()); + recomputeRange(geo->getIndexView()); + recomputeRange(geo->getNormalView()); + for (const auto& view : *geo->getJointWeightViews()) + { + recomputeRange(view.indices); + recomputeRange(view.weights); + } + if (auto pView=geo->getJointOBBView(); pView) + recomputeRange(*pView); + for (const auto& view : *geo->getAuxAttributeViews()) + recomputeRange(view); + } + //! Comparison methods enum E_ERROR_METRIC { @@ -50,6 +120,7 @@ class NBL_API2 IMeshManipulator : public virtual core::IReferenceCounted EEM_QUATERNION, EEM_COUNT }; +#if 0 // TODO: REDO //! Struct used to pass chosen comparison method and epsilon to functions performing error metrics. /** By default epsilon equals 2^-16 and EEM_POSITIONS comparison method is set. @@ -200,53 +271,6 @@ class NBL_API2 IMeshManipulator : public virtual core::IReferenceCounted */ static core::smart_refctd_ptr idxBufferFromTrianglesFanToTriangles(const void* _input, uint32_t& _idxCount, E_INDEX_TYPE _inIndexType, E_INDEX_TYPE _outIndexType); - //! Get amount of polygons in mesh buffer. - /** \param meshbuffer Input mesh buffer - \param Outputted Number of polygons in mesh buffer, if successful. - \return If successfully can provide information */ - template - static inline bool getPolyCount(uint32_t& outCount, const IMeshBuffer* meshbuffer) - { - outCount = 0; - if (!meshbuffer) - return false; - if (!meshbuffer->getPipeline()) - return false; - - const auto& assemblyParams = meshbuffer->getPipeline()->getCachedCreationParams().primitiveAssembly; - const E_PRIMITIVE_TOPOLOGY primType = assemblyParams.primitiveType; - switch (primType) - { - case EPT_POINT_LIST: - outCount = meshbuffer->getIndexCount(); - break; - case EPT_LINE_STRIP: - outCount = meshbuffer->getIndexCount() - 1; - break; - case EPT_LINE_LIST: - outCount = meshbuffer->getIndexCount() / 2; - break; - case EPT_TRIANGLE_STRIP: - outCount = meshbuffer->getIndexCount() - 2; - break; - case EPT_TRIANGLE_FAN: - outCount = meshbuffer->getIndexCount() - 2; - break; - case EPT_TRIANGLE_LIST: - outCount = meshbuffer->getIndexCount() / 3; - break; - case EPT_PATCH_LIST: - outCount = meshbuffer->getIndexCount() / assemblyParams.tessPatchVertCount; - break; - default: - assert(false); // need to implement calculation for more types - return false; - break; - } - - return true; - } - //! static inline std::array getTriangleIndices(const ICPUMeshBuffer* mb, uint32_t triangleIx) { @@ -620,77 +644,141 @@ class NBL_API2 IMeshManipulator : public virtual core::IReferenceCounted params.primitiveType = _newPrimitiveType; } } -#if 0 // TODO: Later - //! Orders meshbuffers according to a predicate - /** - @param _begin non-const iterator to beginning of meshbuffer range - @param _end non-const iterator to ending of meshbuffer range - */ - struct DefaultMeshBufferOrder +#endif +}; + +#if 0 + +//! An interface for easy manipulation of meshes. +/** Scale, set alpha value, flip surfaces, and so on. This exists for fixing +problems with wrong imported or exported meshes quickly after loading. It is +not intended for doing mesh modifications and/or animations during runtime. +*/ +class CMeshManipulator : public IMeshManipulator +{ + struct SAttrib { - public: - template - inline bool operator()(const T& lhs, const T& rhs) const - { - return false; - } + E_FORMAT type; + E_FORMAT prevType; + size_t size; + uint32_t vaid; + size_t offset; + + SAttrib() : type(EF_UNKNOWN), size(0), vaid(ICPUMeshBuffer::MAX_VERTEX_ATTRIB_COUNT) {} + + friend bool operator>(const SAttrib& _a, const SAttrib& _b) { return _a.size > _b.size; } }; - template - static inline void sortMeshBuffers(Iterator _begin, Iterator _end, mesh_buffer_order_t&& order=DefaultMeshBufferOrder()) + struct SAttribTypeChoice { - std::sort(_begin,_end,std::move(order)); - } -#endif - //! Get amount of polygons in mesh. - /** \param meshbuffer Input mesh - \param Outputted Number of polygons in mesh, if successful. - \return If successfully can provide information */ - template - static inline bool getPolyCount(uint32_t& outCount, const IMesh* mesh) + E_FORMAT type; + }; + + public: + static core::smart_refctd_ptr createMeshBufferFetchOptimized(const ICPUMeshBuffer* _inbuffer); + + CQuantNormalCache* getQuantNormalCache() override { return &quantNormalCache; } + CQuantQuaternionCache* getQuantQuaternionCache() override { return &quantQuaternionCache; } + + private: + friend class IMeshManipulator; + + template + static void _filterInvalidTriangles(ICPUMeshBuffer* _input); + + //! Meant to create 32bit index buffer from subrange of index buffer containing 16bit indices. Remember to set to index buffer offset to 0 after mapping buffer resulting from this function. + static inline core::smart_refctd_ptr create32BitFrom16BitIdxBufferSubrange(const uint16_t* _in, uint32_t _idxCount) { - outCount = 0u; - if (!mesh) - return false; + if (!_in) + return nullptr; - bool retval = true; - for (auto mb : mesh->getMeshBuffers()) - { - uint32_t trianglecount; - retval = retval && getPolyCount(trianglecount,mb); - outCount += trianglecount; - } + auto out = ICPUBuffer::create({ sizeof(uint32_t) * _idxCount }); + + auto* outPtr = reinterpret_cast(out->getPointer()); - return retval; + for (uint32_t i=0u; i<_idxCount; ++i) + outPtr[i] = _in[i]; + + return out; } - //! Calculates bounding box of the mesh - template - static inline core::aabbox3df calculateBoundingBox(const IMesh* mesh) + static core::vector findBetterFormatF(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric, CQuantNormalCache& _cache); + + struct SIntegerAttr { - core::aabbox3df aabb(FLT_MAX, FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX); + uint32_t pointer[4]; + }; + static core::vector findBetterFormatI(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric); - auto meshbuffers = mesh->getMeshBuffers(); - for (auto mesh : meshbuffers) - aabb.addInternalBox(mesh->getBoundingBox()); + //E_COMPONENT_TYPE getBestTypeF(bool _normalized, E_COMPONENTS_PER_ATTRIBUTE _cpa, size_t* _outSize, E_COMPONENTS_PER_ATTRIBUTE* _outCpa, const float* _min, const float* _max) const; + static E_FORMAT getBestTypeI(E_FORMAT _originalType, size_t* _outSize, const uint32_t* _min, const uint32_t* _max); + static core::vector findTypesOfProperRangeF(E_FORMAT _type, size_t _sizeThreshold, const float* _min, const float* _max, const SErrorMetric& _errMetric); + + //! Calculates quantization errors and compares them with given epsilon. + /** @returns false when first of calculated errors goes above epsilon or true if reached end without such. */ + static bool calcMaxQuantizationError(const SAttribTypeChoice& _srcType, const SAttribTypeChoice& _dstType, const core::vector& _data, const SErrorMetric& _errMetric, CQuantNormalCache& _cache); + + template + static inline core::smart_refctd_ptr lineStripsToLines(const void* _input, uint32_t& _idxCount) + { + const auto outputSize = _idxCount = (_idxCount - 1) * 2; - return aabb; + auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); + const auto* iptr = reinterpret_cast(_input); + auto* optr = reinterpret_cast(output->getPointer()); + for (uint32_t i = 0, j = 0; i < outputSize;) + { + optr[i++] = iptr[j++]; + optr[i++] = iptr[j]; + } + return output; } - //! Recalculates the cached bounding box of the mesh - template - static inline void recalculateBoundingBox(IMesh* mesh) + template + static inline core::smart_refctd_ptr triangleStripsToTriangles(const void* _input, uint32_t& _idxCount) { - mesh->setBoundingBox(calculateBoundingBox(mesh)); + const auto outputSize = _idxCount = (_idxCount - 2) * 3; + + auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); + const auto* iptr = reinterpret_cast(_input); + auto* optr = reinterpret_cast(output->getPointer()); + for (uint32_t i = 0, j = 0; i < outputSize; j += 2) + { + optr[i++] = iptr[j + 0]; + optr[i++] = iptr[j + 1]; + optr[i++] = iptr[j + 2]; + if (i == outputSize) + break; + optr[i++] = iptr[j + 2]; + optr[i++] = iptr[j + 1]; + optr[i++] = iptr[j + 3]; + } + return output; } + template + static inline core::smart_refctd_ptr trianglesFanToTriangles(const void* _input, uint32_t& _idxCount) + { + const auto outputSize = _idxCount = (_idxCount - 2) * 3; - //! - virtual CQuantNormalCache* getQuantNormalCache() = 0; - virtual CQuantQuaternionCache* getQuantQuaternionCache() = 0; -}; + auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); + const auto* iptr = reinterpret_cast(_input); + auto* optr = reinterpret_cast(output->getPointer()); + for (uint32_t i = 0, j = 1; i < outputSize;) + { + optr[i++] = iptr[0]; + optr[i++] = iptr[j++]; + optr[i++] = iptr[j]; + } + return output; + } -} // end namespace scene -} // end namespace nbl + private: + CQuantNormalCache quantNormalCache; + CQuantQuaternionCache quantQuaternionCache; +}; +#endif +// TODO: Utility in another header for GeometryCollection to compute AABBs, deal with skins (joints), etc. +} // end namespace nbl::asset #endif diff --git a/include/nbl/asset/utils/CSPIRVIntrospector.h b/include/nbl/asset/utils/CSPIRVIntrospector.h index 0d7d678549..eea11652fc 100644 --- a/include/nbl/asset/utils/CSPIRVIntrospector.h +++ b/include/nbl/asset/utils/CSPIRVIntrospector.h @@ -15,7 +15,6 @@ #include "nbl/asset/IShader.h" #include "nbl/asset/ICPUImageView.h" #include "nbl/asset/ICPUComputePipeline.h" -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" #include "nbl/asset/utils/CGLSLCompiler.h" #include "nbl/core/definitions.h" diff --git a/include/nbl/asset/utils/IMeshPacker.h b/include/nbl/asset/utils/IMeshPacker.h deleted file mode 100644 index 3f09062b18..0000000000 --- a/include/nbl/asset/utils/IMeshPacker.h +++ /dev/null @@ -1,636 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_MESH_PACKER_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_PACKER_H_INCLUDED__ - -#include "nbl/asset/utils/IMeshManipulator.h" -#include "nbl/core/math/morton.h" - -namespace nbl -{ -namespace asset -{ - -class IMeshPackerBase : public virtual core::IReferenceCounted -{ - public: - constexpr static uint32_t MAX_TRIANGLES_IN_BATCH_CNT = 21845u; - - struct ReservedAllocationMeshBuffersBase - { - uint32_t mdiAllocationOffset; - uint32_t mdiAllocationReservedCnt; - uint32_t indexAllocationOffset; - uint32_t indexAllocationReservedCnt; - - inline bool isValid() - { - return this->mdiAllocationOffset!=core::GeneralpurposeAddressAllocator::invalid_address; - } - }; - struct PackedMeshBufferData - { - uint32_t mdiParameterOffset; // add to `CCPUMeshPacker::getMultiDrawIndirectBuffer()->getPointer() to get `DrawElementsIndirectCommand_t` address - uint32_t mdiParameterCount; - - inline bool isValid() - { - return this->mdiParameterOffset != core::GeneralpurposeAddressAllocator::invalid_address; - } - }; - - inline uint16_t getMinTriangleCountPerMDI() const { return m_minTriangleCountPerMDIData; } - inline uint16_t getMaxTriangleCountPerMDI() const { return m_maxTriangleCountPerMDIData; } - - protected: - using alctrTraits = core::address_allocator_traits>; - - IMeshPackerBase(uint16_t minTriangleCountPerMDIData, uint16_t maxTriangleCountPerMDIData) - :m_maxTriangleCountPerMDIData(maxTriangleCountPerMDIData), - m_minTriangleCountPerMDIData(minTriangleCountPerMDIData) - { - assert(minTriangleCountPerMDIData <= MAX_TRIANGLES_IN_BATCH_CNT); - assert(maxTriangleCountPerMDIData <= MAX_TRIANGLES_IN_BATCH_CNT); - assert(minTriangleCountPerMDIData <= maxTriangleCountPerMDIData); - assert(minTriangleCountPerMDIData > 0u); - assert(maxTriangleCountPerMDIData > 0u); - }; - - virtual ~IMeshPackerBase() - { - _NBL_ALIGNED_FREE(const_cast(alctrTraits::getReservedSpacePtr(m_MDIDataAlctr))); - _NBL_ALIGNED_FREE(const_cast(alctrTraits::getReservedSpacePtr(m_idxBuffAlctr))); - _NBL_ALIGNED_FREE(const_cast(alctrTraits::getReservedSpacePtr(m_vtxBuffAlctr))); - } - - struct AllocationParamsCommon - { - // Maximum number of 16 bit indicies that may be allocated - size_t indexBuffSupportedCnt = 67108864ull; /* 128MB*/ - - /* Maximum byte size for vertex data allocation - For `CCPUMeshPackerV1` this will be maximum byte size of buffer containing only attributes with EVIR_PER_VERTEX input rate. - For `CCPUMeshPackerV2` this will be maximum byte size of buffer containing attributes with both EVIR_PER_VERTEX and EVIR_PER_INSTANCE input rate. - */ - size_t vertexBuffSupportedByteSize = 134217728ull; /* 128MB*/ - - // Maximum number of MDI structs that may be allocated - size_t MDIDataBuffSupportedCnt = 16777216ull; /* 16MB assuming MDIStructType is DrawElementsIndirectCommand_t*/ - - // Minimum count of 16 bit indicies allocated per allocation - size_t indexBufferMinAllocCnt = 256ull; - - // Minimum bytes of vertex data allocated per allocation - size_t vertexBufferMinAllocByteSize = 32ull; - - // Minimum count of MDI structs allocated per allocation - size_t MDIDataBuffMinAllocCnt = 32ull; - }; - - void initializeCommonAllocators(const AllocationParamsCommon& allocParams) - { - if (allocParams.indexBuffSupportedCnt) - { - - void* resSpcTmp = _NBL_ALIGNED_MALLOC(core::GeneralpurposeAddressAllocator::reserved_size(alignof(uint16_t), allocParams.indexBuffSupportedCnt, allocParams.indexBufferMinAllocCnt), _NBL_SIMD_ALIGNMENT); - assert(resSpcTmp != nullptr); - m_idxBuffAlctr = core::GeneralpurposeAddressAllocator(resSpcTmp, 0u, 0u, alignof(uint16_t), allocParams.indexBuffSupportedCnt, allocParams.indexBufferMinAllocCnt); - } - - if (allocParams.vertexBuffSupportedByteSize) - { - void* resSpcTmp = _NBL_ALIGNED_MALLOC(core::GeneralpurposeAddressAllocator::reserved_size(32u, allocParams.vertexBuffSupportedByteSize, allocParams.vertexBufferMinAllocByteSize), _NBL_SIMD_ALIGNMENT); - assert(resSpcTmp != nullptr); - m_vtxBuffAlctr = core::GeneralpurposeAddressAllocator(resSpcTmp, 0u, 0u, 32u, allocParams.vertexBuffSupportedByteSize, allocParams.vertexBufferMinAllocByteSize); - } - - if (allocParams.MDIDataBuffSupportedCnt) - { - void* resSpcTmp = _NBL_ALIGNED_MALLOC(core::GeneralpurposeAddressAllocator::reserved_size(alignof(std::max_align_t), allocParams.MDIDataBuffSupportedCnt, allocParams.MDIDataBuffMinAllocCnt), _NBL_SIMD_ALIGNMENT); - assert(resSpcTmp != nullptr); - m_MDIDataAlctr = core::GeneralpurposeAddressAllocator(resSpcTmp, 0u, 0u, alignof(std::max_align_t), allocParams.MDIDataBuffSupportedCnt, allocParams.MDIDataBuffMinAllocCnt); - } - } - - void initializeCommonAllocators( - const core::GeneralpurposeAddressAllocator& mdiAlctr, - const core::GeneralpurposeAddressAllocator& idxAlctr, - const core::GeneralpurposeAddressAllocator& vtxAlctr - ) - { - uint32_t alctrBuffSz = alctrTraits::get_total_size(mdiAlctr); - void* resSpcTmp = _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(alctrBuffSz, mdiAlctr), _NBL_SIMD_ALIGNMENT); - m_MDIDataAlctr = core::GeneralpurposeAddressAllocator(alctrBuffSz, mdiAlctr, resSpcTmp); - - alctrBuffSz = alctrTraits::get_total_size(idxAlctr); - resSpcTmp = _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(alctrBuffSz, idxAlctr), _NBL_SIMD_ALIGNMENT); - m_idxBuffAlctr = core::GeneralpurposeAddressAllocator(alctrBuffSz, idxAlctr, resSpcTmp); - - alctrBuffSz = alctrTraits::get_total_size(vtxAlctr); - resSpcTmp = _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(alctrBuffSz, vtxAlctr), _NBL_SIMD_ALIGNMENT); - m_vtxBuffAlctr = core::GeneralpurposeAddressAllocator(alctrBuffSz, vtxAlctr, resSpcTmp); - } - - void free(const ReservedAllocationMeshBuffersBase& rambb) - { - if (rambb.indexAllocationOffset != INVALID_ADDRESS) - m_idxBuffAlctr.free_addr(rambb.indexAllocationOffset,rambb.indexAllocationReservedCnt); - - if (rambb.mdiAllocationOffset != INVALID_ADDRESS) - m_MDIDataAlctr.free_addr(rambb.mdiAllocationOffset,rambb.mdiAllocationReservedCnt); - } - - // - _NBL_STATIC_INLINE_CONSTEXPR uint32_t INVALID_ADDRESS = core::GeneralpurposeAddressAllocator::invalid_address; - - core::GeneralpurposeAddressAllocator m_vtxBuffAlctr; - core::GeneralpurposeAddressAllocator m_idxBuffAlctr; - core::GeneralpurposeAddressAllocator m_MDIDataAlctr; - - const uint16_t m_minTriangleCountPerMDIData; - const uint16_t m_maxTriangleCountPerMDIData; - -}; - -#if 0 // REWRITE -template -class IMeshPacker : public IMeshPackerBase -{ - static_assert(std::is_base_of::value); - -public: - /* - @param minTriangleCountPerMDIData must be <= 21845 - @param maxTriangleCountPerMDIData must be <= 21845 - */ - IMeshPacker(uint16_t minTriangleCountPerMDIData, uint16_t maxTriangleCountPerMDIData) - :IMeshPackerBase(minTriangleCountPerMDIData, maxTriangleCountPerMDIData) - { - } - - //! shrinks byte size of all output buffers, so they are large enough to fit currently allocated contents. Call this function before `instantiateDataStorage` - virtual void shrinkOutputBuffersSize() - { - uint32_t mdiDataBuffNewSize = m_MDIDataAlctr.safe_shrink_size(0u, alctrTraits::max_alignment(m_MDIDataAlctr)); - uint32_t idxBuffNewSize = m_idxBuffAlctr.safe_shrink_size(0u, alctrTraits::max_alignment(m_idxBuffAlctr)); - uint32_t vtxBuffNewSize = m_vtxBuffAlctr.safe_shrink_size(0u, alctrTraits::max_alignment(m_vtxBuffAlctr)); - - const void* oldReserved = alctrTraits::getReservedSpacePtr(m_MDIDataAlctr); - m_MDIDataAlctr = core::GeneralpurposeAddressAllocator(mdiDataBuffNewSize, std::move(m_MDIDataAlctr), _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(mdiDataBuffNewSize, m_MDIDataAlctr), _NBL_SIMD_ALIGNMENT)); - _NBL_ALIGNED_FREE(const_cast(oldReserved)); - - oldReserved = alctrTraits::getReservedSpacePtr(m_idxBuffAlctr); - m_idxBuffAlctr = core::GeneralpurposeAddressAllocator(idxBuffNewSize, std::move(m_idxBuffAlctr), _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(idxBuffNewSize, m_idxBuffAlctr), _NBL_SIMD_ALIGNMENT)); - _NBL_ALIGNED_FREE(const_cast(oldReserved)); - - oldReserved = alctrTraits::getReservedSpacePtr(m_vtxBuffAlctr); - m_vtxBuffAlctr = core::GeneralpurposeAddressAllocator(vtxBuffNewSize, std::move(m_vtxBuffAlctr), _NBL_ALIGNED_MALLOC(alctrTraits::reserved_size(vtxBuffNewSize, m_vtxBuffAlctr), _NBL_SIMD_ALIGNMENT)); - _NBL_ALIGNED_FREE(const_cast(oldReserved)); - } - - //! Returns maximum number of mdi structs needed to draw range of mesh buffers described by range mbBegin .. mbEnd, actual number of MDI structs needed may differ - template - uint32_t calcMDIStructMaxCount(const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) - { - uint32_t acc = 0u; - for (auto mbIt = mbBegin; mbIt != mbEnd; mbIt++) - { - auto mb = *mbIt; - const size_t idxCnt = calcIdxCntAfterConversionToTriangleList(mb); - const uint32_t triCnt = idxCnt / 3; - assert(idxCnt % 3 == 0); - - acc += calcBatchCountBound(triCnt); - } - - return acc; - } - -protected: - virtual ~IMeshPacker() {} - - static inline size_t calcVertexSize(const SVertexInputParams& vtxInputParams, const E_VERTEX_INPUT_RATE inputRate) - { - size_t size = 0ull; - for (size_t i = 0; i < SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT; ++i) - { - if (vtxInputParams.enabledAttribFlags & (1u << i)) - if(vtxInputParams.bindings[i].inputRate == inputRate) - size += asset::getTexelOrBlockBytesize(static_cast(vtxInputParams.attributes[i].format)); - } - - return size; - } - - static inline uint32_t calcVertexCountBoundWithBatchDuplication(const MeshBufferType* meshBuffer) - { - uint32_t triCnt; - if (IMeshManipulator::getPolyCount(triCnt,meshBuffer)) - return triCnt * 3u; - return 0u; - } - - inline uint32_t calcBatchCountBound(uint32_t triCnt) const - { - if (triCnt!=0u) - return (triCnt-1u)/m_minTriangleCountPerMDIData+1u; - return 0u; - } - - struct Triangle - { - uint32_t oldIndices[3]; - }; - - struct TriangleBatches - { - TriangleBatches(uint32_t triCnt) - { - triangles = core::vector(triCnt); - } - - core::vector triangles; - core::vector ranges; - }; - - struct IdxBufferParams - { - SBufferBinding idxBuffer = { 0u, nullptr }; - E_INDEX_TYPE idxType = EIT_UNKNOWN; - }; - - //TODO: functions: constructTriangleBatches, convertIdxBufferToTriangles, deinterleaveAndCopyAttribute and deinterleaveAndCopyPerInstanceAttribute - //will not work with IGPUMeshBuffer as MeshBufferType, move it to new `ICPUMeshPacker` - - TriangleBatches constructTriangleBatches(const MeshBufferType* meshBuffer, IdxBufferParams idxBufferParams, core::aabbox3df*& aabbs) const - { - uint32_t triCnt; - const bool success = IMeshManipulator::getPolyCount(triCnt,meshBuffer); - assert(success); - - const uint32_t batchCnt = calcBatchCountBound(triCnt); - assert(batchCnt != 0u); - - struct MortonTriangle - { - MortonTriangle() = default; - - MortonTriangle(uint16_t fixedPointPos[3], float area) - { - auto tmp = reinterpret_cast(key); - std::copy_n(fixedPointPos,3u,tmp); - tmp[3] = core::Float16Compressor::compress(area); - } - - void complete(float maxArea) - { - auto tmp = reinterpret_cast(key); - const float area = core::Float16Compressor::decompress(tmp[3]); - const float scale = 0.5f; // square root - uint16_t logRelArea = uint16_t(65535.5f+core::clamp(scale*std::log2f(area/maxArea),-65535.5f,0.f)); - key = core::morton4d_encode(tmp[0],tmp[1],tmp[2],logRelArea); - } - - uint64_t key; - }; - - //TODO: use SoA instead (with core::radix_sort): - //core::vector triangles; - //core::vector triangleMortonCodes; - //where `triangles` is member of `TriangleBatch` struct - struct TriangleMortonCodePair - { - Triangle triangle; - MortonTriangle mortonCode; - - inline bool operator<(const TriangleMortonCodePair& other) - { - return this->mortonCode.key < other.mortonCode.key; - } - }; - - TriangleBatches triangleBatches(triCnt); - core::vector triangles(triCnt); //#1 - - core::smart_refctd_ptr mbTmp = core::smart_refctd_ptr_static_cast(meshBuffer->clone()); - mbTmp->setIndexBufferBinding(std::move(idxBufferParams.idxBuffer)); - mbTmp->setIndexType(idxBufferParams.idxType); - mbTmp->getPipeline()->getPrimitiveAssemblyParams().primitiveType = EPT_TRIANGLE_LIST; - - //triangle reordering - { - const core::aabbox3df aabb = IMeshManipulator::calculateBoundingBox(mbTmp.get()); - - uint32_t ix = 0u; - float maxTriangleArea = 0.0f; - for (auto it = triangles.begin(); it != triangles.end(); it++) - { - auto triangleIndices = IMeshManipulator::getTriangleIndices(mbTmp.get(), ix++); - //have to copy there - std::copy(triangleIndices.begin(), triangleIndices.end(), it->triangle.oldIndices); - - core::vectorSIMDf trianglePos[3]; - trianglePos[0] = mbTmp->getPosition(it->triangle.oldIndices[0]); - trianglePos[1] = mbTmp->getPosition(it->triangle.oldIndices[1]); - trianglePos[2] = mbTmp->getPosition(it->triangle.oldIndices[2]); - - const core::vectorSIMDf centroid = ((trianglePos[0] + trianglePos[1] + trianglePos[2]) / 3.0f) - core::vectorSIMDf(aabb.MinEdge.X, aabb.MinEdge.Y, aabb.MinEdge.Z); - uint16_t fixedPointPos[3]; - fixedPointPos[0] = uint16_t(centroid.x * 65535.5f / aabb.getExtent().X); - fixedPointPos[1] = uint16_t(centroid.y * 65535.5f / aabb.getExtent().Y); - fixedPointPos[2] = uint16_t(centroid.z * 65535.5f / aabb.getExtent().Z); - - float area = core::cross(trianglePos[1] - trianglePos[0], trianglePos[2] - trianglePos[0]).x; - it->mortonCode = MortonTriangle(fixedPointPos, area); - - if (area > maxTriangleArea) - maxTriangleArea = area; - } - - //complete morton code - for (auto it = triangles.begin(); it != triangles.end(); it++) - it->mortonCode.complete(maxTriangleArea); - - std::sort(triangles.begin(), triangles.end()); - } - - //copying, after radix_sort this will be removed - //TODO durning radix_sort integration: - //since there will be distinct arrays for triangles and their morton code use `triangleBatches.triangles` instead of #1 - for (uint32_t i = 0u; i < triCnt; i++) - triangleBatches.triangles[i] = triangles[i].triangle; - - //set ranges - Triangle* triangleArrayBegin = triangleBatches.triangles.data(); - Triangle* triangleArrayEnd = triangleArrayBegin + triangleBatches.triangles.size(); - const uint32_t triangleCnt = triangleBatches.triangles.size(); - - //aabb batch division - { - triangleBatches.ranges.push_back(triangleArrayBegin); - for (auto nextTriangle = triangleArrayBegin; nextTriangle < triangleArrayEnd; ) - { - const Triangle* batchBegin = *(triangleBatches.ranges.end() - 1u); - const Triangle* batchEnd = batchBegin + m_minTriangleCountPerMDIData; - - //find min and max edge - core::vector3df_SIMD min(std::numeric_limits::max()); - core::vector3df_SIMD max(-std::numeric_limits::max()); - - auto extendAABB = [&min, &max, &meshBuffer](auto triangleIt) -> void - { - for (uint32_t i = 0u; i < 3u; i++) - { - auto vxPos = meshBuffer->getPosition(triangleIt->oldIndices[i]); - min = core::min(vxPos, min); - max = core::max(vxPos, max); - } - }; - - for (uint32_t i = 0u; i < m_minTriangleCountPerMDIData && nextTriangle != triangleArrayEnd; i++) - extendAABB(nextTriangle++); - - auto halfAreaAABB = [&min, &max]() -> float - { - auto extent = max - min; - return extent.x * extent.y + extent.x * extent.z + extent.y * extent.z; - }; - - constexpr float kGrowthLimit = 1.025f; - float batchArea = halfAreaAABB(); - for (uint16_t i = m_minTriangleCountPerMDIData; nextTriangle != triangleArrayEnd && i < m_maxTriangleCountPerMDIData; i++) - { - if(aabbs) - *aabbs = core::aabbox3df(core::vector3df(min.x, min.y, min.z), core::vector3df(max.x, max.y, max.z)); - - extendAABB(nextTriangle); - float newBatchArea = halfAreaAABB(); - if (newBatchArea > kGrowthLimit* batchArea) - break; - nextTriangle++; - batchArea = newBatchArea; - } - - if (aabbs) - { - if (nextTriangle == triangleArrayEnd || m_minTriangleCountPerMDIData == m_maxTriangleCountPerMDIData) - *aabbs = core::aabbox3df(core::vector3df(min.x, min.y, min.z), core::vector3df(max.x, max.y, max.z)); - aabbs++; - } - - triangleBatches.ranges.push_back(nextTriangle); - } - - } - - return triangleBatches; - } - - static core::unordered_map constructNewIndicesFromTriangleBatchAndUpdateUnifiedIndexBuffer(TriangleBatches& batches, uint32_t batchIdx, uint16_t*& indexBuffPtr) - { - core::unordered_map usedVertices; - core::vector newIdxTris = batches.triangles; - - auto batchBegin = batches.ranges[batchIdx]; - auto batchEnd = batches.ranges[batchIdx + 1]; - - const uint32_t triangleInBatchCnt = std::distance(batchBegin, batchEnd); - const uint32_t idxInBatchCnt = 3u * triangleInBatchCnt; - - uint32_t newIdx = 0u; - for (uint32_t i = 0u; i < triangleInBatchCnt; i++) - { - const Triangle* const triangle = batchBegin + i; - for (int32_t j = 0; j < 3; j++) - { - const uint32_t oldIndex = triangle->oldIndices[j]; - auto result = usedVertices.insert(std::make_pair(oldIndex, newIdx)); - - newIdxTris[i].oldIndices[j] = result.second ? newIdx++ : result.first->second; - } - } - - //TODO: cache optimization - //copy indices into unified index buffer - for (size_t i = 0; i < triangleInBatchCnt; i++) - { - for (int j = 0; j < 3; j++) - { - *indexBuffPtr = newIdxTris[i].oldIndices[j]; - indexBuffPtr++; - } - } - - return usedVertices; - } - - static void deinterleaveAndCopyAttribute(MeshBufferType* meshBuffer, uint16_t attrLocation, const core::unordered_map& usedVertices, uint8_t* dstAttrPtr) - { - const uint8_t* const srcAttrPtr = meshBuffer->getAttribPointer(attrLocation); - SVertexInputParams& mbVtxInputParams = meshBuffer->getPipeline()->getVertexInputParams(); - SVertexInputAttribParams MBAttrib = mbVtxInputParams.attributes[attrLocation]; - SVertexInputBindingParams attribBinding = mbVtxInputParams.bindings[MBAttrib.binding]; - const size_t attrSize = asset::getTexelOrBlockBytesize(static_cast(MBAttrib.format)); - const size_t stride = (attribBinding.stride) == 0 ? attrSize : attribBinding.stride; - - for (auto index : usedVertices) - { - const uint8_t* attrSrc = srcAttrPtr + (index.first * stride); - uint8_t* attrDest = dstAttrPtr + (index.second * attrSize); - memcpy(attrDest, attrSrc, attrSize); - } - } - - static void deinterleaveAndCopyPerInstanceAttribute(MeshBufferType* meshBuffer, uint16_t attrLocation, uint8_t* dstAttrPtr) - { - const uint8_t* const srcAttrPtr = meshBuffer->getAttribPointer(attrLocation); - SVertexInputParams& mbVtxInputParams = meshBuffer->getPipeline()->getVertexInputParams(); - SVertexInputAttribParams MBAttrib = mbVtxInputParams.attributes[attrLocation]; - SVertexInputBindingParams attribBinding = mbVtxInputParams.bindings[MBAttrib.binding]; - const size_t attrSize = asset::getTexelOrBlockBytesize(static_cast(MBAttrib.format)); - const size_t stride = (attribBinding.stride) == 0 ? attrSize : attribBinding.stride; - - const uint32_t insCnt = meshBuffer->getInstanceCount(); - for (uint32_t i = 0u; i < insCnt; i++) - { - const uint8_t* attrSrc = srcAttrPtr + (i * stride); - uint8_t* attrDest = dstAttrPtr + (i * attrSize); - memcpy(attrDest, attrSrc, attrSize); - } - } - - inline uint32_t calcIdxCntAfterConversionToTriangleList(const MeshBufferType* meshBuffer) - { - const auto& params = meshBuffer->getPipeline()->getPrimitiveAssemblyParams(); - - switch (params.primitiveType) - { - case EPT_TRIANGLE_LIST: - case EPT_TRIANGLE_STRIP: - case EPT_TRIANGLE_FAN: - break; - case EPT_POINT_LIST: - case EPT_LINE_LIST: - case EPT_LINE_STRIP: - case EPT_LINE_LIST_WITH_ADJACENCY: - case EPT_LINE_STRIP_WITH_ADJACENCY: - case EPT_TRIANGLE_LIST_WITH_ADJACENCY: - case EPT_TRIANGLE_STRIP_WITH_ADJACENCY: - case EPT_PATCH_LIST: - default: - assert(false); - break; - } - - uint32_t triCnt; - const bool success = IMeshManipulator::getPolyCount(triCnt, meshBuffer); - assert(success); - - return triCnt * 3; - } - inline uint32_t calcIdxCntAfterConversionToTriangleList(const core::smart_refctd_ptr& meshBuffer) - { - return calcIdxCntAfterConversionToTriangleList(meshBuffer.get()); - } - inline uint32_t calcIdxCntAfterConversionToTriangleList(const core::smart_refctd_ptr& meshBuffer) - { - return calcIdxCntAfterConversionToTriangleList(meshBuffer.get()); - } - - std::pair> convertIdxBufferToTriangles(MeshBufferType* meshBuffer) - { - const auto mbIdxBuffer = meshBuffer->getIndexBufferBinding().buffer; - E_INDEX_TYPE idxType = meshBuffer->getIndexType(); - const uint32_t idxCount = meshBuffer->getIndexCount(); - if (idxCount == 0) - return { 0u, nullptr }; - - const bool iota = idxType == EIT_UNKNOWN || !mbIdxBuffer; - core::smart_refctd_ptr idxBufferToProcess; - if (iota) - { - idxBufferToProcess = core::make_smart_refctd_ptr(sizeof(uint32_t) * idxCount); - auto ptr = reinterpret_cast(idxBufferToProcess->getPointer()); - std::iota(ptr, ptr + idxCount, 0u); - idxType = EIT_32BIT; - } - else - { - idxBufferToProcess = mbIdxBuffer; - } - - std::pair> output; - output.first = meshBuffer->getIndexCount(); - - const auto& params = meshBuffer->getPipeline()->getPrimitiveAssemblyParams(); - switch (params.primitiveType) - { - case EPT_TRIANGLE_STRIP: - output.second = IMeshManipulator::idxBufferFromTriangleStripsToTriangles(idxBufferToProcess->getPointer(), output.first, idxType, idxType); - return output; - - case EPT_TRIANGLE_FAN: - output.second = IMeshManipulator::idxBufferFromTrianglesFanToTriangles(idxBufferToProcess->getPointer(), output.first, idxType, idxType); - return output; - - //TODO: packer should return when there is mesh buffer with one of following: - case EPT_TRIANGLE_LIST: - case EPT_POINT_LIST: - case EPT_LINE_LIST: - case EPT_LINE_STRIP: - case EPT_LINE_LIST_WITH_ADJACENCY: - case EPT_LINE_STRIP_WITH_ADJACENCY: - case EPT_TRIANGLE_LIST_WITH_ADJACENCY: - case EPT_TRIANGLE_STRIP_WITH_ADJACENCY: - case EPT_PATCH_LIST: - default: - assert(false); - return { 0u, nullptr }; - } - } - - IdxBufferParams createNewIdxBufferParamsForNonTriangleListTopologies(MeshBufferType* meshBuffer) - { - IdxBufferParams output; - - const auto& mbPrimitiveType = meshBuffer->getPipeline()->getPrimitiveAssemblyParams().primitiveType; - if (mbPrimitiveType == EPT_TRIANGLE_LIST) - { - const auto& mbIdxBuff = meshBuffer->getIndexBufferBinding(); - output.idxBuffer.offset = mbIdxBuff.offset; - output.idxBuffer.buffer = core::smart_refctd_ptr(mbIdxBuff.buffer); - output.idxType = meshBuffer->getIndexType(); - } - else - { - auto newIdxBuffer = convertIdxBufferToTriangles(meshBuffer); - output.idxBuffer.offset = 0u; - output.idxBuffer.buffer = newIdxBuffer.second; - output.idxType = EIT_32BIT; - } - - return output; - } - -protected: - template - struct PackerDataStoreCommon - { - static_assert(std::is_base_of::value); - - core::smart_refctd_ptr MDIDataBuffer; - - inline bool isValid() - { - return this->MDIDataBuffer->getPointer() != nullptr; - } - }; - -}; -#endif -} -} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/utils/IMeshPackerV2.h b/include/nbl/asset/utils/IMeshPackerV2.h deleted file mode 100644 index 89aec7e685..0000000000 --- a/include/nbl/asset/utils/IMeshPackerV2.h +++ /dev/null @@ -1,759 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_I_MESH_PACKER_V2_H_INCLUDED__ -#define __NBL_ASSET_I_MESH_PACKER_V2_H_INCLUDED__ - -#include - -namespace nbl -{ -namespace asset -{ - -class IMeshPackerV2Base -{ -public: - class SupportedFormatsContainer - { - public: - template - void insertFormatsFromMeshBufferRange(MeshBufferIt mbBegin, MeshBufferIt mbEnd) - { - for (auto it = mbBegin; it != mbEnd; it++) - { - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(attrBit & mbVtxInputParams.enabledAttribFlags)) - continue; - - formats.insert(static_cast(mbVtxInputParams.attributes[location].format)); - } - } - } - - inline void insert(E_FORMAT format) - { - formats.insert(format); - } - - inline const core::unordered_set& getFormats() const { return formats; } - - private: - core::unordered_set formats; - }; - -public: - enum E_UTB_ARRAY_TYPE : uint8_t - { - EUAT_FLOAT, - EUAT_INT, - EUAT_UINT, - EUAT_UNKNOWN - }; - struct VirtualAttribConfig - { - VirtualAttribConfig() = default; - - VirtualAttribConfig(const SupportedFormatsContainer& formats) - { - const auto& formatsSet = formats.getFormats(); - for (auto it = formatsSet.begin(); it != formatsSet.end(); it++) - insertAttribFormat(*it); - } - - VirtualAttribConfig(const VirtualAttribConfig& other) - { - std::copy_n(other.utbs, EUAT_UNKNOWN, utbs); - - isUintBufferUsed = other.isUintBufferUsed; - isUvec2BufferUsed = other.isUvec2BufferUsed; - isUvec3BufferUsed = other.isUvec3BufferUsed; - isUvec4BufferUsed = other.isUvec4BufferUsed; - } - - VirtualAttribConfig(VirtualAttribConfig&& other) - { - for (auto i = 0u; i < EUAT_UNKNOWN; i++) - utbs[i] = std::move(other.utbs[i]); - - isUintBufferUsed = other.isUintBufferUsed; - isUvec2BufferUsed = other.isUvec2BufferUsed; - isUvec3BufferUsed = other.isUvec3BufferUsed; - isUvec4BufferUsed = other.isUvec4BufferUsed; - - //other.utbs->clear(); - other.isUintBufferUsed = false; - other.isUvec2BufferUsed = false; - other.isUvec3BufferUsed = false; - other.isUvec4BufferUsed = false; - } - - core::unordered_map utbs[EUAT_UNKNOWN]; - bool isUintBufferUsed = false; - bool isUvec2BufferUsed = false; - bool isUvec3BufferUsed = false; - bool isUvec4BufferUsed = false; - - VirtualAttribConfig& operator=(const VirtualAttribConfig& other) - { - std::copy_n(other.utbs,EUAT_UNKNOWN,utbs); - - isUintBufferUsed = other.isUintBufferUsed; - isUvec2BufferUsed = other.isUvec2BufferUsed; - isUvec3BufferUsed = other.isUvec3BufferUsed; - isUvec4BufferUsed = other.isUvec4BufferUsed; - - return *this; - } - - VirtualAttribConfig& operator=(VirtualAttribConfig&& other) - { - for (auto i=0u; i -class IMeshPackerV2 : public IMeshPacker, public IMeshPackerV2Base -{ - static_assert(std::is_base_of::value); - - using AllocationParams = IMeshPackerBase::AllocationParamsCommon; - - using DescriptorSetLayoutType = typename DescriptorSetType::layout_t; -public: - using base_t = IMeshPacker; - struct AttribAllocParams - { - size_t offset = base_t::INVALID_ADDRESS; - size_t size = 0ull; - }; - - //TODO: REDESIGN - //mdi allocation offset and index allocation offset should be shared - struct ReservedAllocationMeshBuffers : ReservedAllocationMeshBuffersBase - { - AttribAllocParams attribAllocParams[SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT]; - }; - struct VirtualAttribute - { - VirtualAttribute() : va(0u) {}; - - VirtualAttribute(uint16_t arrayElement, uint32_t offset) - :va(0u) - { - assert((offset & 0xF0000000u) == 0u); - - va |= static_cast(arrayElement) << 28u; - va |= offset; - } - - inline uint32_t getArrayElement() const { return core::bitfieldExtract(va,28,4); } - inline void setArrayElement(uint16_t arrayElement) { va = core::bitfieldInsert(va,arrayElement,28,4); } - - inline uint32_t getOffset() const { return core::bitfieldExtract(va,0,28); } - inline void setOffset(uint32_t offset) { va = core::bitfieldInsert(va,offset,0,28); } - - private: - uint32_t va; - }; - - struct CombinedDataOffsetTable - { - VirtualAttribute attribInfo[SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT]; - }; - - struct PackerDataStore : base_t::template PackerDataStoreCommon - { - core::smart_refctd_ptr vertexBuffer; - core::smart_refctd_ptr indexBuffer; - }; - - inline uint32_t getFloatBufferBindingsCnt() const { return m_virtualAttribConfig.utbs[EUAT_FLOAT].size(); } - inline uint32_t getIntBufferBindingsCnt() const { return m_virtualAttribConfig.utbs[EUAT_INT].size(); } - // not including the UTB for the index buffer - inline uint32_t getUintBufferBindingsCnt() const { return m_virtualAttribConfig.utbs[EUAT_UINT].size(); } - - // the following cannot be called before 'instantiateDataStorage' - struct DSLayoutParamsUTB - { - uint32_t usamplersBinding = 0u; - uint32_t fsamplersBinding = 1u; - uint32_t isamplersBinding = 2u; - }; - std::string getGLSLForUTB(uint32_t descriptorSet = 0u, const DSLayoutParamsUTB& params = {}) - { - std::string result = "#define _NBL_VG_DESCRIPTOR_SET " + std::to_string(descriptorSet) + '\n'; - - result += "#define _NBL_VG_UINT_BUFFERS\n"; - result += "#define _NBL_VG_UINT_BUFFERS_BINDING " + std::to_string(params.usamplersBinding) + '\n'; - result += "#define _NBL_VG_UINT_BUFFERS_COUNT " + std::to_string(1u+getUintBufferBindingsCnt()) + '\n'; - if (getFloatBufferBindingsCnt()) - { - result += "#define _NBL_VG_FLOAT_BUFFERS\n"; - result += "#define _NBL_VG_FLOAT_BUFFERS_BINDING " + std::to_string(params.fsamplersBinding) + '\n'; - result += "#define _NBL_VG_FLOAT_BUFFERS_COUNT " + std::to_string(getFloatBufferBindingsCnt()) + '\n'; - } - if (getIntBufferBindingsCnt()) - { - result += "#define _NBL_VG_INT_BUFFERS\n"; - result += "#define _NBL_VG_INT_BUFFERS_BINDING " + std::to_string(params.isamplersBinding) + '\n'; - result += "#define _NBL_VG_INT_BUFFERS_COUNT " + std::to_string(getFloatBufferBindingsCnt()) + '\n'; - } - - return result; - } - - inline uint32_t getDSlayoutBindingsForUTB(typename DescriptorSetLayoutType::SBinding* outBindings, const DSLayoutParamsUTB& params = {}) const - { - const uint32_t bindingCount = 1u + // for the always present uint index buffer - (getFloatBufferBindingsCnt() ? 1u : 0u) + - (getIntBufferBindingsCnt() ? 1u : 0u); - - if (outBindings) - { - auto* bnd = outBindings; - auto fillBinding = [&bnd](uint32_t binding, uint32_t count) - { - bnd->binding = binding; - bnd->count = count; - bnd->stageFlags = asset::ISpecializedShader::ESS_ALL; - bnd->type = asset::IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER; - bnd->samplers = nullptr; - bnd++; - }; - - fillBinding(params.usamplersBinding,getUintBufferBindingsCnt()+1u); - if (getFloatBufferBindingsCnt()) - fillBinding(params.fsamplersBinding,getFloatBufferBindingsCnt()); - if (getIntBufferBindingsCnt()) - fillBinding(params.isamplersBinding,getIntBufferBindingsCnt()); - } - return bindingCount; - } - - inline std::pair getDescriptorSetWritesForUTB( - typename DescriptorSetType::SWriteDescriptorSet* outWrites, typename DescriptorSetType::SDescriptorInfo* outInfo, DescriptorSetType* dstSet, - std::function(core::smart_refctd_ptr&&,E_FORMAT)> createBufferView, const DSLayoutParamsUTB& params = {} - ) const - { - const uint32_t writeCount = getDSlayoutBindingsForUTB(nullptr); - const uint32_t infoCount = 1u + // for the index buffer - getFloatBufferBindingsCnt() + - getIntBufferBindingsCnt() + - getUintBufferBindingsCnt(); - if (!outWrites || !outInfo) - return std::make_pair(writeCount, infoCount); - - auto* info = outInfo; - auto fillInfoStruct = [&](E_UTB_ARRAY_TYPE utbArrayType) - { - for (auto virtualAttribData : m_virtualAttribConfig.utbs[utbArrayType]) - { - E_FORMAT format = virtualAttribData.first; - switch (format) - { - case EF_A2B10G10R10_SNORM_PACK32: - format = EF_R32_UINT; - break; - default: - break; - } - info[virtualAttribData.second].desc = createBufferView(core::smart_refctd_ptr(m_packerDataStore.vertexBuffer),format); - info[virtualAttribData.second].buffer.offset = 0u; - info[virtualAttribData.second].buffer.size = m_packerDataStore.vertexBuffer->getSize(); - } - info += m_virtualAttribConfig.utbs[utbArrayType].size(); - }; - - auto* write = outWrites; - auto writeBinding = [&write,dstSet,&info](uint32_t binding, uint32_t count) - { - write->binding = binding; - write->arrayElement = 0u; - write->count = count; - write->descriptorType = asset::IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER; - write->dstSet = dstSet; - write->info = info; - write++; - }; - - writeBinding(params.usamplersBinding, 1u + getUintBufferBindingsCnt()); - if (getUintBufferBindingsCnt()) - fillInfoStruct(E_UTB_ARRAY_TYPE::EUAT_UINT); - info->desc = createBufferView(core::smart_refctd_ptr(m_packerDataStore.indexBuffer),EF_R16_UINT); - info->buffer.offset = 0u; - info->buffer.size = m_packerDataStore.indexBuffer->getSize(); - info++; - if (getFloatBufferBindingsCnt()) - { - writeBinding(params.fsamplersBinding, getFloatBufferBindingsCnt()); - fillInfoStruct(E_UTB_ARRAY_TYPE::EUAT_FLOAT); - } - if (getIntBufferBindingsCnt()) - { - writeBinding(params.isamplersBinding, getIntBufferBindingsCnt()); - fillInfoStruct(E_UTB_ARRAY_TYPE::EUAT_INT); - } - - return std::make_pair(writeCount, infoCount); - } - - // the following cannot be called before 'instantiateDataStorage' - struct DSLayoutParamsSSBO - { - uint32_t uintBufferBinding = 0u; - uint32_t uvec2BufferBinding = 1u; - uint32_t uvec3BufferBinding = 2u; - uint32_t uvec4BufferBinding = 3u; - uint32_t indexBufferBinding = 4u; - }; - inline std::string getGLSLForSSBO(uint32_t descriptorSet = 0u, const DSLayoutParamsSSBO& params = {}) - { - std::string result = "#define _NBL_VG_USE_SSBO\n"; - result += "#define _NBL_VG_SSBO_DESCRIPTOR_SET " + std::to_string(descriptorSet) + '\n'; - - if (m_virtualAttribConfig.isUintBufferUsed) - { - result += "#define _NBL_VG_USE_SSBO_UINT\n"; - result += "#define _NBL_VG_SSBO_UINT_BINDING " + std::to_string(params.uintBufferBinding) + '\n'; - } - if (m_virtualAttribConfig.isUvec2BufferUsed) - { - result += "#define _NBL_VG_USE_SSBO_UVEC2\n"; - result += "#define _NBL_VG_SSBO_UVEC2_BINDING " + std::to_string(params.uvec2BufferBinding) + '\n'; - } - if (m_virtualAttribConfig.isUvec3BufferUsed) - { - result += "#define _NBL_VG_USE_SSBO_UVEC3\n"; - result += "#define _NBL_VG_SSBO_UVEC3_BINDING " + std::to_string(params.uvec3BufferBinding) + '\n'; - } - if (m_virtualAttribConfig.isUvec4BufferUsed) - { - result += "#define _NBL_VG_USE_SSBO_UVEC4\n"; - result += "#define _NBL_VG_SSBO_UVEC4_BINDING " + std::to_string(params.uvec4BufferBinding) + '\n'; - } - - result += "#define _NBL_VG_USE_SSBO_INDEX\n"; - result += "#define _NBL_VG_SSBO_INDEX_BINDING " + std::to_string(params.indexBufferBinding) + '\n'; - return result; - } - - inline uint32_t getDSlayoutBindingsForSSBO(typename DescriptorSetLayoutType::SBinding* outBindings, const DSLayoutParamsSSBO& params = {}) const - { - const uint32_t bindingCount = 1 + // for the index buffer - m_virtualAttribConfig.isUintBufferUsed + - m_virtualAttribConfig.isUvec2BufferUsed + - m_virtualAttribConfig.isUvec3BufferUsed + - m_virtualAttribConfig.isUvec4BufferUsed; - - if (outBindings) - { - auto* bnd = outBindings; - auto fillBinding = [&bnd](uint32_t binding) - { - bnd->binding = binding; - bnd->count = 1u; - bnd->stageFlags = asset::ISpecializedShader::ESS_ALL; - bnd->type = asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER; - bnd->samplers = nullptr; - bnd++; - }; - - if (m_virtualAttribConfig.isUintBufferUsed) - fillBinding(params.uintBufferBinding); - if (m_virtualAttribConfig.isUvec2BufferUsed) - fillBinding(params.uvec2BufferBinding); - if (m_virtualAttribConfig.isUvec3BufferUsed) - fillBinding(params.uvec3BufferBinding); - if (m_virtualAttribConfig.isUvec4BufferUsed) - fillBinding(params.uvec4BufferBinding); - fillBinding(params.indexBufferBinding); - } - return bindingCount; - } - - // info count is always 2 - inline uint32_t getDescriptorSetWritesForSSBO( - typename DescriptorSetType::SWriteDescriptorSet* outWrites, typename DescriptorSetType::SDescriptorInfo* outInfo, DescriptorSetType* dstSet, - const DSLayoutParamsSSBO& params = {} - ) const - { - const uint32_t writeCount = getDSlayoutBindingsForSSBO(nullptr); - if (!outWrites || !outInfo) - return writeCount; - - auto* write = outWrites; - auto info = outInfo; - auto fillWriteStruct = [&](uint32_t binding) - { - write->binding = binding; - write->arrayElement = 0u; - write->count = 1u; - write->descriptorType = IDescriptor::E_TYPE::ET_STORAGE_BUFFER; - write->dstSet = dstSet; - write->info = info; - write++; - }; - if (m_virtualAttribConfig.isUintBufferUsed) - fillWriteStruct(params.uintBufferBinding); - if (m_virtualAttribConfig.isUvec2BufferUsed) - fillWriteStruct(params.uvec2BufferBinding); - if (m_virtualAttribConfig.isUvec3BufferUsed) - fillWriteStruct(params.uvec3BufferBinding); - if (m_virtualAttribConfig.isUvec4BufferUsed) - fillWriteStruct(params.uvec4BufferBinding); - info->desc = m_packerDataStore.vertexBuffer; - info->buffer.offset = 0u; - info->buffer.size = m_packerDataStore.vertexBuffer->getSize(); - info++; - - fillWriteStruct(params.indexBufferBinding); - info->desc = m_packerDataStore.indexBuffer; - info->buffer.offset = 0u; - info->buffer.size = m_packerDataStore.indexBuffer->getSize(); - info++; - - return writeCount; - } - - template - bool alloc(ReservedAllocationMeshBuffers* rambOut, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd); - - void free(const ReservedAllocationMeshBuffers* rambIn, uint32_t meshBuffersToFreeCnt) - { - for (uint32_t i = 0u; i < meshBuffersToFreeCnt; i++) - { - const ReservedAllocationMeshBuffers& ramb = rambIn[i]; - - const ReservedAllocationMeshBuffers* const ramb = rambIn + i; - - if (ramb->indexAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_idxBuffAlctr.free_addr(ramb->indexAllocationOffset, ramb->indexAllocationReservedCnt); - - if (ramb->mdiAllocationOffset != base_t::INVALID_ADDRESS) - base_t::m_MDIDataAlctr.free_addr(ramb->mdiAllocationOffset, ramb->mdiAllocationReservedCnt); - - for (uint32_t j = 0; j < SVertexInputParams::MAX_VERTEX_ATTRIB_COUNT; j++) - { - const AttribAllocParams& attrAllocParams = ramb->attribAllocParams[j]; - if (attrAllocParams.offset != base_t::INVALID_ADDRESS) - base_t::m_vtxBuffAlctr.free_addr(attrAllocParams.offset, attrAllocParams.size); - } - } - } - - inline const PackerDataStore& getPackerDataStore() const { return m_packerDataStore; } - - const core::GeneralpurposeAddressAllocator& getMDIAllocator() const { return base_t::m_MDIDataAlctr; } - const core::GeneralpurposeAddressAllocator& getIndexAllocator() const { return base_t::m_idxBuffAlctr; } - const core::GeneralpurposeAddressAllocator& getVertexAllocator() const { return base_t::m_vtxBuffAlctr; } - -protected: - IMeshPackerV2(const AllocationParams& allocParams, const SupportedFormatsContainer& formats, uint16_t minTriangleCountPerMDIData, uint16_t maxTriangleCountPerMDIData) - : base_t(minTriangleCountPerMDIData, maxTriangleCountPerMDIData), - IMeshPackerV2Base(formats) - { - base_t::initializeCommonAllocators(allocParams); - }; - template - explicit IMeshPackerV2(const IMeshPackerV2* otherMp) - : base_t(otherMp->getMinTriangleCountPerMDI(),otherMp->getMaxTriangleCountPerMDI()), - IMeshPackerV2Base(otherMp->getVirtualAttribConfig()) - { - base_t::initializeCommonAllocators( - otherMp->getMDIAllocator(), - otherMp->getIndexAllocator(), - otherMp->getVertexAllocator() - ); - }; - - template - void freeAllocatedAddressesOnAllocFail(ReservedAllocationMeshBuffers* rambOut, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) - { - size_t i = 0ull; - for (auto it = mbBegin; it != mbEnd; it++) - { - ReservedAllocationMeshBuffers& ramb = *(rambOut + i); - - if (ramb.indexAllocationOffset == base_t::INVALID_ADDRESS) - return; - - base_t::m_idxBuffAlctr.free_addr(ramb.indexAllocationOffset, ramb.indexAllocationReservedCnt); - - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(attrBit & mbVtxInputParams.enabledAttribFlags)) - continue; - - if (ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - return; - - base_t::m_vtxBuffAlctr.free_addr(ramb.attribAllocParams[location].offset, ramb.attribAllocParams[location].size); - } - - if (ramb.mdiAllocationOffset == base_t::INVALID_ADDRESS) - return; - - base_t::m_MDIDataAlctr.free_addr(ramb.mdiAllocationOffset, ramb.mdiAllocationReservedCnt); - - i++; - } - } - - PackerDataStore m_packerDataStore; - -}; - -template -template -bool IMeshPackerV2::alloc(ReservedAllocationMeshBuffers* rambOut, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) -{ - size_t i = 0ull; - for (auto it = mbBegin; it != mbEnd; it++) - { - ReservedAllocationMeshBuffers& ramb = *(rambOut + i); - const size_t idxCnt = base_t::calcIdxCntAfterConversionToTriangleList(*it); - const size_t maxVtxCnt = base_t::calcVertexCountBoundWithBatchDuplication(*it); - const uint32_t insCnt = (*it)->getInstanceCount(); - - //allocate indices - ramb.indexAllocationOffset = base_t::m_idxBuffAlctr.alloc_addr(idxCnt, 1u); - if (ramb.indexAllocationOffset == base_t::INVALID_ADDRESS) - { - freeAllocatedAddressesOnAllocFail(rambOut, mbBegin, mbEnd); - return false; - } - ramb.indexAllocationReservedCnt = idxCnt; - - //allocate vertices - const auto& mbVtxInputParams = (*it)->getPipeline()->getVertexInputParams(); - for (uint16_t attrBit = 0x0001, location = 0; location < SVertexInputParams::MAX_ATTR_BUF_BINDING_COUNT; attrBit <<= 1, location++) - { - if (!(attrBit & mbVtxInputParams.enabledAttribFlags)) - continue; - - const E_FORMAT attribFormat = static_cast(mbVtxInputParams.attributes[location].format); - - if (!m_virtualAttribConfig.isFormatSupported(attribFormat)) - return false; - - const uint32_t attribSize = asset::getTexelOrBlockBytesize(attribFormat); - const uint32_t binding = mbVtxInputParams.attributes[location].binding; - const E_VERTEX_INPUT_RATE inputRate = mbVtxInputParams.bindings[binding].inputRate; - - if (inputRate == EVIR_PER_VERTEX) - { - const uint32_t allocByteSize = maxVtxCnt * attribSize; - ramb.attribAllocParams[location].offset = base_t::m_vtxBuffAlctr.alloc_addr(allocByteSize, attribSize); - ramb.attribAllocParams[location].size = allocByteSize; - - if(ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - { - freeAllocatedAddressesOnAllocFail(rambOut, mbBegin, mbEnd); - return false; - } - } - else if (inputRate == EVIR_PER_INSTANCE) - { - const uint32_t allocByteSize = insCnt * attribSize; - ramb.attribAllocParams[location].offset = base_t::m_vtxBuffAlctr.alloc_addr(allocByteSize, attribSize); - ramb.attribAllocParams[location].size = allocByteSize; - - if (ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - { - freeAllocatedAddressesOnAllocFail(rambOut, mbBegin, mbEnd); - return false; - } - } - - if (ramb.attribAllocParams[location].offset == base_t::INVALID_ADDRESS) - return false; - } - - //allocate MDI structs - const uint32_t minIdxCntPerPatch = base_t::m_minTriangleCountPerMDIData * 3; - size_t possibleMDIStructsNeededCnt = (idxCnt + minIdxCntPerPatch - 1) / minIdxCntPerPatch; - - ramb.mdiAllocationOffset = base_t::m_MDIDataAlctr.alloc_addr(possibleMDIStructsNeededCnt, 1u); - if (ramb.mdiAllocationOffset == base_t::INVALID_ADDRESS) - { - freeAllocatedAddressesOnAllocFail(rambOut, mbBegin, mbEnd); - return false; - } - ramb.mdiAllocationReservedCnt = possibleMDIStructsNeededCnt; - - i++; - } - - return true; -} -#endif -} -} - -#endif \ No newline at end of file diff --git a/include/nbl/asset/utils/phmap_deserialization.h b/include/nbl/asset/utils/phmap_deserialization.h index fa4dd1637e..790b729705 100644 --- a/include/nbl/asset/utils/phmap_deserialization.h +++ b/include/nbl/asset/utils/phmap_deserialization.h @@ -1,9 +1,8 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_PHMAP_DESERIALIZATION_H_INCLUDED__ -#define __NBL_ASSET_PHMAP_DESERIALIZATION_H_INCLUDED__ +#ifndef _NBL_ASSET_PHMAP_DESERIALIZATION_H_INCLUDED_ +#define _NBL_ASSET_PHMAP_DESERIALIZATION_H_INCLUDED_ #include "nbl/core/declarations.h" #include "nbl/asset/ICPUBuffer.h" @@ -20,7 +19,7 @@ class CBufferPhmapInputArchive } // TODO: protect against reading out of the buffer range - bool load(char* p, size_t sz) + bool loadBinary(void* p, size_t sz) { memcpy(p, buffPtr, sz); buffPtr += sz; @@ -29,7 +28,7 @@ class CBufferPhmapInputArchive } template - typename std::enable_if::value,bool>::type load(V* v) + typename std::enable_if::value,bool>::type loadBinary(V* v) { memcpy(reinterpret_cast(v), buffPtr, sizeof(V)); buffPtr += sizeof(V); diff --git a/include/nbl/asset/utils/phmap_serialization.h b/include/nbl/asset/utils/phmap_serialization.h index 9a6e368c6b..fe7ce64196 100644 --- a/include/nbl/asset/utils/phmap_serialization.h +++ b/include/nbl/asset/utils/phmap_serialization.h @@ -1,9 +1,8 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_PHMAP_SERIALIZATION_H_INCLUDED__ -#define __NBL_ASSET_PHMAP_SERIALIZATION_H_INCLUDED__ +#ifndef _NBL_ASSET_PHMAP_SERIALIZATION_H_INCLUDED_ +#define _NBL_ASSET_PHMAP_SERIALIZATION_H_INCLUDED_ #include "nbl/core/declarations.h" #include "nbl/asset/ICPUBuffer.h" @@ -20,7 +19,7 @@ class CBufferPhmapOutputArchive } // TODO: protect against writing out of bounds as defined by SBufferRange - bool dump(const char* p, size_t sz) + bool saveBinary(const void* p, size_t sz) { memcpy(bufferPtr, p, sz); bufferPtr += sz; @@ -29,7 +28,7 @@ class CBufferPhmapOutputArchive } template - typename std::enable_if::value,bool>::type dump(const V& v) + typename std::enable_if::value,bool>::type saveBinary(const V& v) { memcpy(bufferPtr, reinterpret_cast(&v), sizeof(V)); bufferPtr += sizeof(V); diff --git a/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl b/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl index b1d33f097b..bc78154bfa 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl @@ -15,9 +15,9 @@ struct matrix final : private glm::mat using Base::Base; using Base::operator[]; - // For assigning to same dimension use implicit ctor, and even then only allow for dimension truncation - template requires ((X!=N || Y!=M) && X>=N && Y>=M) - explicit matrix(matrix const& m) : Base(reinterpret_cast const&>(m)) {} + // For assigning to same dimension and type use implicit ctor, and even then only allow for dimension truncation + template requires ((!std::is_same_v || X!=N || Y!=M) && X>=N && Y>=M) + explicit matrix(matrix const& m) : Base(reinterpret_cast const&>(m)) {} matrix(matrix const&) = default; explicit matrix(Base const& base) : Base(base) {} diff --git a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl index d4b2ea5b7e..a8134ee871 100644 --- a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl +++ b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl @@ -4,70 +4,180 @@ #ifndef _NBL_BUILTIN_HLSL_MATH_LINALG_FAST_AFFINE_INCLUDED_ #define _NBL_BUILTIN_HLSL_MATH_LINALG_FAST_AFFINE_INCLUDED_ + +#include +#include #include -#if 0 // TODO -vec4 pseudoMul4x4with3x1(in mat4 m, in vec3 v) + +namespace nbl { - return m[0] * v.x + m[1] * v.y + m[2] * v.z + m[3]; -} -vec3 pseudoMul3x4with3x1(in mat4x3 m, in vec3 v) +namespace hlsl { - return m[0] * v.x + m[1] * v.y + m[2] * v.z + m[3]; -} -mat4x3 pseudoMul4x3with4x3(in mat4x3 lhs, in mat4x3 rhs) // TODO: change name to 3x4with3x4 +namespace math { - mat4x3 result; - for (int i = 0; i < 4; i++) - result[i] = lhs[0] * rhs[i][0] + lhs[1] * rhs[i][1] + lhs[2] * rhs[i][2]; - result[3] += lhs[3]; - return result; -} -mat4 pseudoMul4x4with4x3(in mat4 proj, in mat4x3 tform) +namespace linalg { - mat4 result; - for (int i = 0; i < 4; i++) - result[i] = proj[0] * tform[i][0] + proj[1] * tform[i][1] + proj[2] * tform[i][2]; - result[3] += proj[3]; - return result; -} +// TODO: move to macros +#ifdef __HLSL_VERSION +#define NBL_UNROLL [[unroll]] +#else +#define NBL_UNROLL +#endif -// useful for fast computation of a Normal Matrix (you just need to remember to normalize the transformed normal because of the missing divide by the determinant) -mat3 sub3x3TransposeCofactors(in mat3 sub3x3) +// Multiply matrices as-if extended to be filled with identity elements +template +matrix promoted_mul(NBL_CONST_REF_ARG(matrix) lhs, NBL_CONST_REF_ARG(matrix) rhs) { - return mat3( - cross(sub3x3[1],sub3x3[2]), - cross(sub3x3[2],sub3x3[0]), - cross(sub3x3[0],sub3x3[1]) - ); + matrix retval; + // NxM = NxR RxM + // out[i][j] == dot(row[i],col[j]) + // out[i][j] == lhs[i][0]*col[j][0]+...+lhs[i][3]*col[j][3] + // col[a][b] == (rhs^T)[b][a] + // out[i][j] == lhs[i][0]*rhs[0][j]+...+lhs[i][3]*rhs[3][j] + // out[i] == lhs[i][0]*rhs[0]+...+lhs[i][3]*rhs[3] + NBL_UNROLL for (uint32_t i=0; i acc = i >(0); + if (i>=Q) + acc[i] = T(1); + // multiply if not outside of `lhs` matrix + // otherwise the diagonal element is just unity + if (i +vector promoted_mul(NBL_CONST_REF_ARG(matrix) lhs, const vector v) { - sub3x3TransposeCofactors = sub3x3TransposeCofactors(sub3x3); - return floatBitsToUint(dot(sub3x3[0],sub3x3TransposeCofactors[0]))&0x80000000u; + vector retval; + // Nx1 = NxM Mx1 + { + matrix rhs; + // one can safely discard elements of `v[i]` where `i

=M`, because to contribute `lhs` would need to have `M>=P` + NBL_UNROLL for (uint32_t i=0; i tmp = promoted_mul(lhs,rhs); + NBL_UNROLL for (uint32_t i=0; i +struct cofactors_base; -// use this if you anticipate flipped/mirrored models -vec3 fastNormalTransform(in uint signFlipMask, in mat3 sub3x3TransposeCofactors, in vec3 normal) +template +struct cofactors_base { - vec3 tmp = sub3x3TransposeCofactors*normal; - const float tmpLenRcp = inversesqrt(dot(tmp,tmp)); - return tmp*uintBitsToFloat(floatBitsToUint(tmpLenRcp)^signFlipMask); -} -#endif + using matrix_t = matrix; + using vector_t = vector; + + static inline cofactors_base create(NBL_CONST_REF_ARG(matrix_t) val) + { + cofactors_base retval; + + retval.transposed = matrix_t( + hlsl::cross(val[1],val[2]), + hlsl::cross(val[2],val[0]), + hlsl::cross(val[0],val[1]) + ); + + return retval; + } + + // + inline matrix_t get() NBL_CONST_MEMBER_FUNC + { + return hlsl::transpose(transposed); + } + + // + inline vector_t normalTransform(const vector_t n) NBL_CONST_MEMBER_FUNC + { + const vector_t tmp = hlsl::mul(transposed,n); + return hlsl::normalize(tmp); + } + + matrix_t transposed; +}; + +// variant that cares about flipped/mirrored transforms +template +struct cofactors +{ + using pseudo_base_t = cofactors_base; + using matrix_t = typename pseudo_base_t::matrix_t; + using vector_t = typename pseudo_base_t::vector_t; + using mask_t = unsigned_integer_of_size_t; + + static inline cofactors create(NBL_CONST_REF_ARG(matrix_t) val) + { + cofactors retval; + retval.composed = pseudo_base_t::create(val); + + const T det = hlsl::dot(val[0],retval.composed.transposed[0]); + + const mask_t SignBit = 1; + SignBit = SignBit<<(sizeof(mask_t)*8-1); + retval.signFlipMask = bit_cast(det) & SignBit; + + return retval; + } + + // + inline vector_t normalTransform(const vector_t n) NBL_CONST_MEMBER_FUNC + { + const vector_t tmp = hlsl::mul(composed.transposed,n); + const T rcpLen = hlsl::rsqrt(hlsl::dot(tmp,tmp)); + return tmp*bit_cast(bit_cast(rcpLen)^determinantSignMask); + } + + cofactors_base composed; + mask_t determinantSignMask; +}; // -template NBL_REQUIRES(is_matrix_v) // TODO: allow any matrix type AND our emulated ones -Mat3x4 pseudoInverse3x4(NBL_CONST_REF_ARG(Mat3x4) tform) +template) // TODO: allow any matrix type AND our emulated ones +Mat3x4 pseudoInverse3x4(NBL_CONST_REF_ARG(Mat3x4) tform, NBL_CONST_REF_ARG(matrix,3,3>) sub3x3Inv) { - const matrix,3,3> sub3x3Inv = inverse(mat3(tform)); Mat3x4 retval; retval[0] = sub3x3Inv[0]; retval[1] = sub3x3Inv[1]; retval[2] = sub3x3Inv[2]; - retval[3] = -sub3x3Inv*tform[3]; + retval[3] = -hlsl::mul(sub3x3Inv,tform[3]); return retval; } +template) // TODO: allow any matrix type AND our emulated ones +Mat3x4 pseudoInverse3x4(NBL_CONST_REF_ARG(Mat3x4) tform) +{ + return pseudoInverse3x4(tform,inverse(matrix,3,3>(tform))); +} + +} +} +} +} #endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/shapes/aabb.hlsl b/include/nbl/builtin/hlsl/shapes/aabb.hlsl index 9c24eff0a5..0aea99f2e0 100644 --- a/include/nbl/builtin/hlsl/shapes/aabb.hlsl +++ b/include/nbl/builtin/hlsl/shapes/aabb.hlsl @@ -4,8 +4,9 @@ #ifndef _NBL_BUILTIN_HLSL_SHAPES_AABB_INCLUDED_ #define _NBL_BUILTIN_HLSL_SHAPES_AABB_INCLUDED_ -#include - +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/limits.hlsl" +#include "nbl/builtin/hlsl/shapes/util.hlsl" namespace nbl { @@ -14,38 +15,85 @@ namespace hlsl namespace shapes { -struct AABB_t +template +struct AABB { + using scalar_t = Scalar; + using point_t = vector; + + static AABB create() + { + AABB retval; + retval.minVx = promote(numeric_limits::max); + retval.maxVx = promote(numeric_limits::lowest); + return retval; + } + // - void addPoint(const float3 pt) + void addPoint(const point_t pt) { - minVx = min(pt, minVx); - maxVx = max(pt, maxVx); + minVx = hlsl::min(pt,minVx); + maxVx = hlsl::max(pt,maxVx); } // - float3 getExtent() + point_t getExtent() { return maxVx - minVx; } // - float getVolume() + Scalar getVolume() { - const float3 extent = AABB_t::getExtent(); + const point_t extent = getExtent(); return extent.x * extent.y * extent.z; } // returns the corner of the AABB which has the most positive dot product - float3 getFarthestPointInFront(const float3 plane) + point_t getFarthestPointInFront(const point_t planeNormal) + { + return hlsl::mix(maxVx,minVx,planeNormal < promote(0.f)); + } + + point_t minVx; + point_t maxVx; +}; + +namespace util +{ +namespace impl +{ +template +struct intersect_helper> +{ + using type = AABB; + + static inline type __call(NBL_CONST_REF_ARG(type) lhs, NBL_CONST_REF_ARG(type) rhs) { - return lerp(maxVx, minVx, plane(lhs.minVx,rhs.minVx); + retval.maxVx = hlsl::min(lhs.maxVx,rhs.maxVx); + return retval; } +}; +template +struct union_helper> +{ + using type = AABB; - float3 minVx; - float3 maxVx; + static inline type __call(NBL_CONST_REF_ARG(type) lhs, NBL_CONST_REF_ARG(type) rhs) + { + type retval; + retval.minVx = hlsl::min(lhs.minVx,rhs.minVx); + retval.maxVx = hlsl::max(lhs.maxVx,rhs.maxVx); + return retval; + } }; +} +} -struct nbl_glsl_shapes_CompressedAABB_t +#if 0 // experimental +#include +struct CompressedAABB_t { // AABB_t decompress() @@ -59,6 +107,7 @@ struct nbl_glsl_shapes_CompressedAABB_t uint2 minVx18E7S3; uint2 maxVx18E7S3; }; +#endif } diff --git a/include/nbl/builtin/hlsl/shapes/beziers.hlsl b/include/nbl/builtin/hlsl/shapes/beziers.hlsl index 4a8acc4fd0..662ac22453 100644 --- a/include/nbl/builtin/hlsl/shapes/beziers.hlsl +++ b/include/nbl/builtin/hlsl/shapes/beziers.hlsl @@ -14,6 +14,7 @@ #include #include +#include namespace nbl { diff --git a/include/nbl/builtin/hlsl/shapes/line.hlsl b/include/nbl/builtin/hlsl/shapes/line.hlsl index db59d2ef9d..60a0718d64 100644 --- a/include/nbl/builtin/hlsl/shapes/line.hlsl +++ b/include/nbl/builtin/hlsl/shapes/line.hlsl @@ -13,72 +13,97 @@ namespace hlsl { namespace shapes { - template - struct Line - { - using scalar_t = float_t; - using float_t2 = vector; - using float_t3 = vector; - using float_t2x2 = matrix; + +template +struct Line +{ + using scalar_t = float_t; + using float_t2 = vector; + using float_t3 = vector; + using float_t2x2 = matrix; - float_t2 P0; - float_t2 P1; + float_t2 P0; + float_t2 P1; - struct ArcLengthCalculator + struct ArcLengthCalculator + { + float_t len; + + static ArcLengthCalculator construct(NBL_CONST_REF_ARG(Line) segment) { - float_t len; - - static ArcLengthCalculator construct(NBL_CONST_REF_ARG(Line) segment) - { - ArcLengthCalculator ret = { segment.getLength() }; - return ret; - } - - float_t calcArcLen(float_t t) - { - return len * t; - } - - float_t calcArcLenInverse(NBL_CONST_REF_ARG(Line) segment, float_t min, float_t max, float_t arcLen, float_t accuracyThreshold, float_t hint) - { - return arcLen / len; - } - }; - - static Line construct(NBL_CONST_REF_ARG(float_t2) P0, NBL_CONST_REF_ARG(float_t2) P1) - { - Line ret = { P0, P1 }; + ArcLengthCalculator ret = { segment.getLength() }; return ret; } - float_t2 evaluate(float_t t) NBL_CONST_MEMBER_FUNC + float_t calcArcLen(float_t t) { - return t * (P1 - P0) + P0; + return len * t; } - - float_t getLength() NBL_CONST_MEMBER_FUNC + + float_t calcArcLenInverse(NBL_CONST_REF_ARG(Line) segment, float_t min, float_t max, float_t arcLen, float_t accuracyThreshold, float_t hint) { - return length(P1 - P0); + return arcLen / len; } + }; - NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxCandidates = 1u; - using Candidates = vector; + static Line construct(NBL_CONST_REF_ARG(float_t2) P0, NBL_CONST_REF_ARG(float_t2) P1) + { + Line ret = { P0, P1 }; + return ret; + } - Candidates getClosestCandidates(NBL_CONST_REF_ARG(float_t2) pos) NBL_CONST_MEMBER_FUNC - { - Candidates ret; - float_t2 p0p1 = P1 - P0; - float_t2 posp0 = pos - P0; - ret[0] = dot(posp0, p0p1) / dot(p0p1, p0p1); - return ret; - } + float_t2 evaluate(float_t t) NBL_CONST_MEMBER_FUNC + { + return t * (P1 - P0) + P0; + } + + float_t getLength() NBL_CONST_MEMBER_FUNC + { + return length(P1 - P0); + } + + NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxCandidates = 1u; + using Candidates = vector; + + Candidates getClosestCandidates(NBL_CONST_REF_ARG(float_t2) pos) NBL_CONST_MEMBER_FUNC + { + Candidates ret; + float_t2 p0p1 = P1 - P0; + float_t2 posp0 = pos - P0; + ret[0] = dot(posp0, p0p1) / dot(p0p1, p0p1); + return ret; + } + + float_t2x2 getLocalCoordinateSpace(float_t t) NBL_CONST_MEMBER_FUNC + { + float_t2 d = normalize(P1 - P0); + return float_t2x2(d.x, d.y, -d.y, d.x); + } +}; + +namespace util +{ +template +static vector LineLineIntersection(NBL_CONST_REF_ARG(vector) P1, NBL_CONST_REF_ARG(vector) V1, NBL_CONST_REF_ARG(vector) P2, NBL_CONST_REF_ARG(vector) V2) +{ + typedef vector float_t2; + + float_t denominator = V1.y * V2.x - V1.x * V2.y; + vector diff = P1 - P2; + float_t numerator = dot(float_t2(V2.y, -V2.x), float_t2(diff.x, diff.y)); + + if (abs(denominator) < 1e-15 && abs(numerator) < 1e-15) + { + // are parallel and the same + return (P1 + P2) / 2.0; + } + + float_t t = numerator / denominator; + float_t2 intersectionPoint = P1 + t * V1; + return intersectionPoint; +} +} - float_t2x2 getLocalCoordinateSpace(float_t t) NBL_CONST_MEMBER_FUNC - { - float_t2 d = normalize(P1 - P0); - return float_t2x2(d.x, d.y, -d.y, d.x); - } - }; } } } diff --git a/include/nbl/builtin/hlsl/shapes/util.hlsl b/include/nbl/builtin/hlsl/shapes/util.hlsl index f3f45c87e0..a32ac7858b 100644 --- a/include/nbl/builtin/hlsl/shapes/util.hlsl +++ b/include/nbl/builtin/hlsl/shapes/util.hlsl @@ -16,26 +16,20 @@ namespace shapes namespace util { -template -static vector LineLineIntersection(NBL_CONST_REF_ARG(vector) P1, NBL_CONST_REF_ARG(vector) V1, NBL_CONST_REF_ARG(vector) P2, NBL_CONST_REF_ARG(vector) V2) +namespace impl { - typedef vector float_t2; - - float_t denominator = V1.y * V2.x - V1.x * V2.y; - vector diff = P1 - P2; - float_t numerator = dot(float_t2(V2.y, -V2.x), float_t2(diff.x, diff.y)); - - if (abs(denominator) < 1e-15 && abs(numerator) < 1e-15) - { - // are parallel and the same - return (P1 + P2) / 2.0; - } - - float_t t = numerator / denominator; - float_t2 intersectionPoint = P1 + t * V1; - return intersectionPoint; +template +struct intersect_helper; +template +struct union_helper; } +template +T intersect(NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs) {return impl::intersect_helper::__call(lhs,rhs);} +// union is a keyword in C++ +template +T union_(NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs) {return impl::union_helper::__call(lhs,rhs);} + } } } diff --git a/include/nbl/ext/MitsubaLoader/CMitsubaSerializedMetadata.h b/include/nbl/ext/MitsubaLoader/CMitsubaSerializedMetadata.h index 7be0bb89d6..a8082cafef 100644 --- a/include/nbl/ext/MitsubaLoader/CMitsubaSerializedMetadata.h +++ b/include/nbl/ext/MitsubaLoader/CMitsubaSerializedMetadata.h @@ -1,19 +1,15 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_C_MITSUBA_SERIALIZED_PIPELINE_METADATA_H_INCLUDED_ +#define _NBL_C_MITSUBA_SERIALIZED_PIPELINE_METADATA_H_INCLUDED_ -#ifndef __NBL_C_MITSUBA_SERIALIZED_PIPELINE_METADATA_H_INCLUDED__ -#define __NBL_C_MITSUBA_SERIALIZED_PIPELINE_METADATA_H_INCLUDED__ -#include "nbl/asset/ICPURenderpassIndependentPipeline.h" -#include "nbl/asset/ICPUMesh.h" +#include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/metadata/IAssetMetadata.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader + +namespace nbl::ext::MitsubaLoader { class CMitsubaSerializedMetadata final : public asset::IAssetMetadata @@ -82,7 +78,4 @@ class CMitsubaSerializedMetadata final : public asset::IAssetMetadata }; } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CSerializedLoader.h b/include/nbl/ext/MitsubaLoader/CSerializedLoader.h index 8bf540e2ef..4ded07d423 100644 --- a/include/nbl/ext/MitsubaLoader/CSerializedLoader.h +++ b/include/nbl/ext/MitsubaLoader/CSerializedLoader.h @@ -1,21 +1,20 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_EXT_MITSUBA_C_SERIALIZED_LOADER_H_INCLUDED_ +#define _NBL_EXT_MITSUBA_C_SERIALIZED_LOADER_H_INCLUDED_ -#ifndef __C_SERIALIZED_LOADER_H_INCLUDED__ -#define __C_SERIALIZED_LOADER_H_INCLUDED__ + +#include "nbl/system/declarations.h" #include "nbl/asset/asset.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader + +namespace nbl::ext::MitsubaLoader { //! Meshloader capable of loading obj meshes. -class CSerializedLoader final : public asset::IRenderpassIndependentPipelineLoader +class CSerializedLoader final : public asset::IGeometryLoader { protected: //! Destructor @@ -23,7 +22,7 @@ class CSerializedLoader final : public asset::IRenderpassIndependentPipelineLoad public: //! Constructor - CSerializedLoader(asset::IAssetManager* _manager) : IRenderpassIndependentPipelineLoader(_manager) {} + CSerializedLoader(asset::IAssetManager* _manager) : IGeometryLoader() {} inline bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger = nullptr) const override { @@ -42,8 +41,6 @@ class CSerializedLoader final : public asset::IRenderpassIndependentPipelineLoad return ext; } - inline uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MESH; } - //! creates/loads an animated mesh from the file. asset::SAssetBundle loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; @@ -71,8 +68,4 @@ class CSerializedLoader final : public asset::IRenderpassIndependentPipelineLoad } -} -} - #endif - diff --git a/include/nbl/video/IGPUMesh.h b/include/nbl/video/IGPUMesh.h deleted file mode 100644 index 1ad2a74d9a..0000000000 --- a/include/nbl/video/IGPUMesh.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_VIDEO_I_GPU_MESH_H_INCLUDED_ -#define _NBL_VIDEO_I_GPU_MESH_H_INCLUDED_ - -#if 0 // rewrite -#include "nbl/asset/IMesh.h" -#include "nbl/video/IGPUMeshBuffer.h" - -namespace nbl -{ -namespace video -{ - -class IGPUMesh final : public asset::IMesh -{ - using MeshBufferRefContainer = core::smart_refctd_dynamic_array>; - MeshBufferRefContainer m_meshBuffers; - - public: - IGPUMesh(uint32_t meshBufferCount) : m_meshBuffers(core::make_refctd_dynamic_array(meshBufferCount)) - { - } - - template - IGPUMesh(MeshBufferIterator begin, MeshBufferIterator end) : IGPUMesh(std::distance(begin,end)) - { - std::copy(begin,end,m_meshBuffers->begin()); - } - - inline auto getMeshBufferIterator() - { - return m_meshBuffers->data(); - } - - inline core::SRange getMeshBuffers() const override - { - auto begin = reinterpret_cast(m_meshBuffers->data()); - return core::SRange(begin,begin+m_meshBuffers->size()); - } - inline core::SRange getMeshBuffers() override - { - auto begin = reinterpret_cast(m_meshBuffers->data()); - return core::SRange(begin,begin+m_meshBuffers->size()); - } -}; - -} -} -#endif - -#endif \ No newline at end of file diff --git a/include/nbl/video/IGPUMeshBuffer.h b/include/nbl/video/IGPUMeshBuffer.h deleted file mode 100644 index 89f7aa13bf..0000000000 --- a/include/nbl/video/IGPUMeshBuffer.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h -#ifndef _NBL_VIDEO_I_GPU_MESH_BUFFER_H_INCLUDED_ -#define _NBL_VIDEO_I_GPU_MESH_BUFFER_H_INCLUDED_ - -#if 0 // rewrite -#include "nbl/asset/asset.h" - -#include - -#include "nbl/video/IGPUBuffer.h" -#include "nbl/video/IGPUDescriptorSet.h" - - -namespace nbl::video -{ - -class IGPUMeshBuffer final : public asset::IMeshBuffer -{ - public: - using base_t = asset::IMeshBuffer; - - using base_t::base_t; -}; - -} // end namespace nbl::video -#endif - -#endif - - diff --git a/include/nbl/video/IGPUPolygonGeometry.h b/include/nbl/video/IGPUPolygonGeometry.h new file mode 100644 index 0000000000..171f871d01 --- /dev/null +++ b/include/nbl/video/IGPUPolygonGeometry.h @@ -0,0 +1,66 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_VIDEO_I_GPU_POLYGON_GEOMETRY_H_INCLUDED_ +#define _NBL_VIDEO_I_GPU_POLYGON_GEOMETRY_H_INCLUDED_ + + +#include "nbl/asset/IPolygonGeometry.h" + +#include "nbl/video/IGPUBuffer.h" + + +namespace nbl::video +{ + +// +class IGPUPolygonGeometry final : public asset::IPolygonGeometry +{ + using base_t = asset::IPolygonGeometry; + + public: + using SDataView = base_t::SDataView; + struct SCreationParams + { + SDataView positionView = {}; + SDataView jointOBBView = {}; + SDataView indexView = {}; + const IIndexingCallback* indexing = nullptr; + SDataView normalView = {}; + std::span jointWeightViews = {}; + std::span auxAttributeViews = {}; + uint32_t jointCount = 0; + }; + static inline core::smart_refctd_ptr create(SCreationParams&& params) + { + auto retval = core::smart_refctd_ptr(new IGPUPolygonGeometry(),core::dont_grab); + retval->m_positionView = params.positionView; + if (params.jointCount) + retval->m_jointOBBView = params.jointOBBView; + retval->m_indexView = params.indexView; + retval->m_indexing = params.indexing; + if (params.jointCount) + retval->m_jointWeightViews.insert(retval->m_jointWeightViews.begin(),params.jointWeightViews.begin(),params.jointWeightViews.end()); + retval->m_normalView = params.normalView; + retval->m_auxAttributeViews.insert(retval->m_auxAttributeViews.begin(),params.auxAttributeViews.begin(),params.auxAttributeViews.end()); + retval->m_jointCount = params.jointCount; + if (!retval->valid()) + return nullptr; + return retval; + } + + // passthrough +#if 0 + inline const SDataView& getNormalView() const {return base_t::getNormalView();} + inline const core::vector& getJointWeightViews() const {return base_t::getJointWeightViews();} + inline const core::vector& getAuxAttributeViews() const {return base_t::getAuxAttributeViews();} +#endif + + private: + inline IGPUPolygonGeometry() = default; + inline ~IGPUPolygonGeometry() = default; +}; + +} + +#endif \ No newline at end of file diff --git a/include/nbl/video/ILogicalDevice.h b/include/nbl/video/ILogicalDevice.h index def3ee0979..6298afeb27 100644 --- a/include/nbl/video/ILogicalDevice.h +++ b/include/nbl/video/ILogicalDevice.h @@ -366,6 +366,11 @@ class NBL_API2 ILogicalDevice : public core::IReferenceCounted, public IDeviceMe // Create an ImageView that can actually be used by shaders (@see ICPUImageView) inline core::smart_refctd_ptr createImageView(IGPUImageView::SCreationParams&& params) { + if (!params.image) + { + NBL_LOG_ERROR("The image is null"); + return nullptr; + } if (!params.image->wasCreatedBy(this)) { NBL_LOG_ERROR("The image was not created by this device"); diff --git a/include/nbl/video/asset_traits.h b/include/nbl/video/asset_traits.h index 5b085b2d3b..faf5322798 100644 --- a/include/nbl/video/asset_traits.h +++ b/include/nbl/video/asset_traits.h @@ -19,6 +19,8 @@ #include "nbl/video/IGPUImageView.h" #include "nbl/asset/ICPUAccelerationStructure.h" #include "nbl/video/IGPUAccelerationStructure.h" +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/video/IGPUPolygonGeometry.h" namespace nbl::video @@ -228,6 +230,20 @@ struct asset_traits }; +template<> +struct asset_traits +{ + // the asset type + using asset_t = asset::ICPUPolygonGeometry; + // depends on `ICPUBuffer` + constexpr static inline bool HasChildren = true; + // the video type + using video_t = IGPUPolygonGeometry; + // lookup type + using lookup_t = const video_t*; +}; + + /* TODO template<> struct asset_traits; diff --git a/include/nbl/video/declarations.h b/include/nbl/video/declarations.h index 2fdfe28e3c..37f2f864bf 100644 --- a/include/nbl/video/declarations.h +++ b/include/nbl/video/declarations.h @@ -41,7 +41,6 @@ #include "nbl/video/utilities/CAssetConverter.h" //VT -//#include "nbl/video/CGPUMeshPackerV2.h" //#include "nbl/video/IGPUVirtualTexture.h" diff --git a/include/nbl/video/utilities/CAssetConverter.h b/include/nbl/video/utilities/CAssetConverter.h index 935b79b1e5..3f0225a78e 100644 --- a/include/nbl/video/utilities/CAssetConverter.h +++ b/include/nbl/video/utilities/CAssetConverter.h @@ -50,8 +50,9 @@ class CAssetConverter : public core::IReferenceCounted asset::ICPUComputePipeline, asset::ICPURenderpass, asset::ICPUGraphicsPipeline, - asset::ICPUDescriptorSet + asset::ICPUDescriptorSet, //asset::ICPUFramebuffer doesn't exist yet XD + asset::ICPUPolygonGeometry >; struct SCreationParams @@ -92,11 +93,11 @@ class CAssetConverter : public core::IReferenceCounted { #define PATCH_IMPL_BOILERPLATE(ASSET_TYPE) using this_t = patch_impl_t; \ public: \ - inline patch_impl_t() = default; \ - inline patch_impl_t(const this_t& other) = default; \ - inline patch_impl_t(this_t&& other) = default; \ - inline this_t& operator=(const this_t& other) = default; \ - inline this_t& operator=(this_t&& other) = default; \ + constexpr inline patch_impl_t() = default; \ + constexpr inline patch_impl_t(const this_t& other) = default; \ + constexpr inline patch_impl_t(this_t&& other) = default; \ + constexpr inline this_t& operator=(const this_t& other) = default; \ + constexpr inline this_t& operator=(this_t&& other) = default; \ patch_impl_t(const ASSET_TYPE* asset); \ bool valid(const ILogicalDevice* device) @@ -334,11 +335,11 @@ class CAssetConverter : public core::IReferenceCounted using this_t = patch_impl_t; public: - inline patch_impl_t() = default; - inline patch_impl_t(const this_t& other) = default; - inline patch_impl_t(this_t&& other) = default; - inline this_t& operator=(const this_t& other) = default; - inline this_t& operator=(this_t&& other) = default; + constexpr inline patch_impl_t() = default; + constexpr inline patch_impl_t(const this_t& other) = default; + constexpr inline patch_impl_t(this_t&& other) = default; + constexpr inline this_t& operator=(const this_t& other) = default; + constexpr inline this_t& operator=(this_t&& other) = default; using usage_flags_t = IGPUImage::E_USAGE_FLAGS; // slightly weird constructor because it deduces the metadata from subusages, so need the subusages right away, not patched later @@ -414,6 +415,29 @@ class CAssetConverter : public core::IReferenceCounted bool invalid = true; }; + template<> + struct NBL_API2 patch_impl_t + { + public: + PATCH_IMPL_BOILERPLATE(asset::ICPUPolygonGeometry); + + using usage_flags_t = IGPUBuffer::E_USAGE_FLAGS; + // assume programmable pulling for all attributes + core::bitflag positionBufferUsages = usage_flags_t::EUF_SHADER_DEVICE_ADDRESS_BIT; + // assume nothing + core::bitflag indexBufferUsages = usage_flags_t::EUF_NONE; + core::bitflag otherBufferUsages = usage_flags_t::EUF_SHADER_DEVICE_ADDRESS_BIT; + + protected: + inline std::pair combine(const this_t& other) const + { + this_t retval = *this; + retval.indexBufferUsages |= other.indexBufferUsages; + retval.positionBufferUsages |= other.positionBufferUsages; + retval.otherBufferUsages |= other.otherBufferUsages; + return {true,retval}; + } + }; #undef PATCH_IMPL_BOILERPLATE // The default specialization provides simple equality operations and hash operations, this will work as long as your patch_impl_t doesn't: // - use a container like `core::vector`, etc. @@ -426,12 +450,12 @@ class CAssetConverter : public core::IReferenceCounted // forwarding using base_t::base_t; - inline patch_t(const this_t& other) : base_t(other) {} - inline patch_t(this_t&& other) : base_t(std::move(other)) {} - inline patch_t(base_t&& other) : base_t(std::move(other)) {} + constexpr inline patch_t(const this_t& other) : base_t(other) {} + constexpr inline patch_t(this_t&& other) : base_t(std::move(other)) {} + constexpr inline patch_t(base_t&& other) : base_t(std::move(other)) {} - inline this_t& operator=(const this_t& other) = default; - inline this_t& operator=(this_t&& other) = default; + constexpr inline this_t& operator=(const this_t& other) = default; + constexpr inline this_t& operator=(this_t&& other) = default; // The assumption is we'll only ever be combining valid patches together. // Returns: whether the combine op was a success, DOESN'T MEAN the result is VALID! @@ -537,6 +561,7 @@ class CAssetConverter : public core::IReferenceCounted virtual const patch_t* operator()(const lookup_t&) const = 0; virtual const patch_t* operator()(const lookup_t&) const = 0; virtual const patch_t* operator()(const lookup_t&) const = 0; + virtual const patch_t* operator()(const lookup_t&) const = 0; // certain items are not patchable, so there's no `patch_t` with non zero size inline const patch_t* operator()(const lookup_t& unpatchable) const @@ -555,7 +580,7 @@ class CAssetConverter : public core::IReferenceCounted { return unpatchable.patch; } - + // while other things are top level assets in the graph and `operator()` would never be called on their patch }; // `cacheMistrustLevel` is how deep from `asset` do we start trusting the cache to contain correct non stale hashes @@ -668,6 +693,7 @@ class CAssetConverter : public core::IReferenceCounted bool operator()(lookup_t); bool operator()(lookup_t); bool operator()(lookup_t); + bool operator()(lookup_t); }; // diff --git a/include/nbl/video/utilities/CGPUMeshPackerV2.h b/include/nbl/video/utilities/CGPUMeshPackerV2.h deleted file mode 100644 index c6e900e824..0000000000 --- a/include/nbl/video/utilities/CGPUMeshPackerV2.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_GPU_MESH_PACKER_V2_H_INCLUDED__ -#define __NBL_ASSET_C_GPU_MESH_PACKER_V2_H_INCLUDED__ - -#include -#include -#include -#include -#include - -using namespace nbl::video; - -namespace nbl -{ -namespace video -{ - -#if 0 // REWRITE -template -class CGPUMeshPackerV2 final : public asset::IMeshPackerV2 -{ - using base_t = asset::IMeshPackerV2; - using Triangle = typename base_t::Triangle; - using TriangleBatches = typename base_t::TriangleBatches; - - public: - using AllocationParams = typename base_t::AllocationParamsCommon; - using PackerDataStore = typename base_t::PackerDataStore; - using ReservedAllocationMeshBuffers = typename base_t::ReservedAllocationMeshBuffers; - using AttribAllocParams = typename base_t::AttribAllocParams; - - public: - CGPUMeshPackerV2(ILogicalDevice* driver, const AllocationParams& allocParams, const asset::IMeshPackerV2Base::SupportedFormatsContainer& formats, uint16_t minTriangleCountPerMDIData = 256u, uint16_t maxTriangleCountPerMDIData = 1024u) - : base_t(allocParams, formats, minTriangleCountPerMDIData, maxTriangleCountPerMDIData), m_driver(driver) - { - m_utilities = core::make_smart_refctd_ptr(core::smart_refctd_ptr(driver)); - } - - // TODO: protect against empty cpuMP (no allocations and then shrinked) - CGPUMeshPackerV2(ILogicalDevice* driver, IQueue* queue, const asset::CCPUMeshPackerV2* cpuMP) - : base_t(cpuMP), m_driver(driver) - { - // TODO: protect against unitiliazed storage of cpuMP - const auto& cpuMDIBuff = cpuMP->getPackerDataStore().MDIDataBuffer; - const auto& cpuIdxBuff = cpuMP->getPackerDataStore().indexBuffer; - const auto& cpuVtxBuff = cpuMP->getPackerDataStore().vertexBuffer; - - m_utilities = core::make_smart_refctd_ptr(core::smart_refctd_ptr(driver)); - - // TODO: call this->instantiateDataStorage() here and then copy CPU data to the initialized storage - base_t::m_packerDataStore.MDIDataBuffer = m_utilities->createFilledDeviceLocalBufferOnDedMem(queue, cpuMDIBuff->getSize(),cpuMDIBuff->getPointer()); - base_t::m_packerDataStore.indexBuffer = m_utilities->createFilledDeviceLocalBufferOnDedMem(queue, cpuIdxBuff->getSize(),cpuIdxBuff->getPointer()); - base_t::m_packerDataStore.vertexBuffer = m_utilities->createFilledDeviceLocalBufferOnDedMem(queue, cpuVtxBuff->getSize(),cpuVtxBuff->getPointer()); - } - - void instantiateDataStorage(); - - template - bool commit(typename base_t::PackedMeshBufferData* pmbdOut, ReservedAllocationMeshBuffers* rambIn, core::aabbox3df* aabbs, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd); - - inline std::pair getDescriptorSetWritesForUTB( - IGPUDescriptorSet::SWriteDescriptorSet* outWrites, IGPUDescriptorSet::SDescriptorInfo* outInfo, IGPUDescriptorSet* dstSet, - const typename base_t::DSLayoutParamsUTB& params = {} - ) const - { - auto createBufferView = [&](core::smart_refctd_ptr&& buff, asset::E_FORMAT format) -> core::smart_refctd_ptr - { - return m_driver->createBufferView(buff.get(),format); - }; - return base_t::getDescriptorSetWritesForUTB(outWrites,outInfo,dstSet,createBufferView,params); - } - private: - core::smart_refctd_ptr m_utilities; - ILogicalDevice* m_driver; - -}; - -template -void CGPUMeshPackerV2::instantiateDataStorage() -{ - // Update to NewBufferAPI when porting - // auto createAndAllocateBuffer = [&](size_t size) -> auto - // { - // video::IGPUBuffer::SCreationParams creationParams = {}; - // creationParams.size = size; - // creationParams.usage = asset::IBuffer::EUF_STORAGE_BUFFER_BIT; ??? - // auto buffer = params.device->createBuffer(creationParams); - // auto mreqs = buffer->getMemoryReqs(); - // mreqs.memoryTypeBits &= params.device->getPhysicalDevice()->getDeviceLocalMemoryTypeBits(); - // auto gpubufMem = params.device->allocate(mreqs, buffer.get()); - // return buffer; - // }; - - const uint32_t MDIDataBuffByteSize = base_t::m_MDIDataAlctr.get_total_size() * sizeof(MDIStructType); - const uint32_t idxBuffByteSize = base_t::m_idxBuffAlctr.get_total_size() * sizeof(uint16_t); - const uint32_t vtxBuffByteSize = base_t::m_vtxBuffAlctr.get_total_size(); - - base_t::m_packerDataStore.MDIDataBuffer = m_driver->createDeviceLocalGPUBufferOnDedMem(MDIDataBuffByteSize); - base_t::m_packerDataStore.indexBuffer = m_driver->createDeviceLocalGPUBufferOnDedMem(idxBuffByteSize); - base_t::m_packerDataStore.vertexBuffer = m_driver->createDeviceLocalGPUBufferOnDedMem(vtxBuffByteSize); -} - -template -template -bool CGPUMeshPackerV2::commit(typename base_t::PackedMeshBufferData* pmbdOut, ReservedAllocationMeshBuffers* rambIn, core::aabbox3df* aabbs, const MeshBufferIterator mbBegin, const MeshBufferIterator mbEnd) -{ - assert(0); - return false; -} -#endif -} -} - -#endif \ No newline at end of file diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index 2dddc74f77..adf320a5ee 100755 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -53,9 +53,9 @@ option(_NBL_COMPILE_WITH_MTL_LOADER_ "Compile with MTL Loader" OFF) #default off option(_NBL_COMPILE_WITH_OBJ_LOADER_ "Compile with OBJ Loader" OFF) #default off until Material Compiler 2 #option(_NBL_COMPILE_WITH_OBJ_WRITER_ "Compile with OBJ Writer" ON) uncomment when writer exists option(_NBL_COMPILE_WITH_STL_LOADER_ "Compile with STL Loader" OFF) #default off until Material Compiler 2 -option(_NBL_COMPILE_WITH_STL_WRITER_ "Compile with STL Writer" ON) +option(_NBL_COMPILE_WITH_STL_WRITER_ "Compile with STL Writer" OFF) option(_NBL_COMPILE_WITH_PLY_LOADER_ "Compile with PLY Loader" OFF) #default off until Material Compiler 2 -option(_NBL_COMPILE_WITH_PLY_WRITER_ "Compile with PLY Writer" ON) +option(_NBL_COMPILE_WITH_PLY_WRITER_ "Compile with PLY Writer" OFF) option(_NBL_COMPILE_WITH_JPG_LOADER_ "Compile with JPG Loader" ON) option(_NBL_COMPILE_WITH_JPG_WRITER_ "Compile with JPG Writer" ON) option(_NBL_COMPILE_WITH_PNG_LOADER_ "Compile with PNG Loader" ON) @@ -156,9 +156,9 @@ set(NBL_ASSET_SOURCES ${NBL_ROOT_PATH}/src/nbl/asset/IAssetManager.cpp ${NBL_ROOT_PATH}/src/nbl/asset/ICPUDescriptorSet.cpp ${NBL_ROOT_PATH}/src/nbl/asset/ICPUImage.cpp + ${NBL_ROOT_PATH}/src/nbl/asset/ICPUPolygonGeometry.cpp ${NBL_ROOT_PATH}/src/nbl/asset/interchange/IAssetWriter.cpp ${NBL_ROOT_PATH}/src/nbl/asset/interchange/IAssetLoader.cpp - ${NBL_ROOT_PATH}/src/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.cpp # Shaders ${NBL_ROOT_PATH}/src/nbl/asset/utils/ISPIRVOptimizer.cpp @@ -179,8 +179,8 @@ set(NBL_ASSET_SOURCES ${NBL_ROOT_PATH}/src/nbl/asset/utils/CForsythVertexCacheOptimizer.cpp ${NBL_ROOT_PATH}/src/nbl/asset/utils/CSmoothNormalGenerator.cpp ${NBL_ROOT_PATH}/src/nbl/asset/utils/CGeometryCreator.cpp - ${NBL_ROOT_PATH}/src/nbl/asset/utils/CMeshManipulator.cpp - ${NBL_ROOT_PATH}/src/nbl/asset/utils/COverdrawMeshOptimizer.cpp + ${NBL_ROOT_PATH}/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp + ${NBL_ROOT_PATH}/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.cpp ${NBL_ROOT_PATH}/src/nbl/asset/utils/CSmoothNormalGenerator.cpp # Mesh loaders @@ -350,21 +350,18 @@ if(NBL_CPACK_NO_BUILD_DIRECTORY_MODULES) target_compile_definitions(Nabla PUBLIC NBL_CPACK_NO_BUILD_DIRECTORY_MODULES) endif() -if(NBL_COMPILER_DYNAMIC_RUNTIME) - set_property(TARGET Nabla PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") -else() - set_property(TARGET Nabla PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -endif() - -target_compile_definitions(Nabla PRIVATE __NBL_BUILDING_NABLA__) - -target_link_options(Nabla INTERFACE # proxy to downstream targets - $<$: - $<$:/DELAYLOAD:$> - /DELAYLOAD:dxcompiler.dll - > +target_compile_definitions(Nabla + PUBLIC _DXC_DLL_="${DXC_DLL}" + PRIVATE __NBL_BUILDING_NABLA__ ) +if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES MSVC) + target_link_options(Nabla + INTERFACE /DELAYLOAD:$ + PRIVATE /DELAYLOAD:dxcompiler.dll + ) +endif() + if (ANDROID) add_library(android_native_app_glue STATIC ${ANDROID_NDK_ROOT_PATH}/sources/android/native_app_glue/android_native_app_glue.c @@ -379,9 +376,12 @@ if (ANDROID) ) endif() +set(NBL_ASSEMBLY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$/devshgraphicsprogramming.nabla") if(NOT NBL_STATIC_BUILD) - set(NBL_ASSEMBLY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$/devshgraphicsprogramming.nabla" CACHE INTERNAL "" FORCE) set_target_properties(Nabla PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NBL_ASSEMBLY_DIRECTORY}) + target_compile_definitions(Nabla PUBLIC + _NABLA_DLL_NAME_="$>";_NABLA_OUTPUT_DIR_="${NBL_ASSEMBLY_DIRECTORY}" + ) endif() ## Set up 3rdparty deps @@ -591,10 +591,10 @@ endif() # Include dirs for self target_include_directories(Nabla PUBLIC - ${CMAKE_CURRENT_BINARY_DIR}/include - ${NBL_ROOT_PATH}/include + "${CMAKE_CURRENT_BINARY_DIR}/include" + "${NBL_ROOT_PATH}/include" ${COMMON_INCLUDE_DIRS} - ${THIRD_PARTY_SOURCE_DIR} + "${THIRD_PARTY_SOURCE_DIR}" #those also goes as PUBLIC because of examples "$<$:${NABLA_CONF_DIR_DEBUG}>" "$<$:${NABLA_CONF_DIR_RELEASE}>" @@ -698,9 +698,13 @@ if (MSVC) target_compile_options(Nabla PUBLIC /bigobj) endif() -#precompiled headers if(NBL_PCH) - target_precompile_headers(Nabla PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/pch.h") + target_precompile_headers(Nabla + # private as nothing from source directory should ever leak to downstream targets! + # NOTE: currently our whole public and private interface is broken + # and private headers leak to public includes + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/pch.h" + ) endif() # extensions @@ -774,3 +778,5 @@ else() endif() nbl_install_program_spec("${DXC_DLL}" "nbl/3rdparty/dxc") + +NBL_ADJUST_FOLDERS(src) \ No newline at end of file diff --git a/src/nbl/asset/IAssetManager.cpp b/src/nbl/asset/IAssetManager.cpp index 26b029585b..344429f227 100644 --- a/src/nbl/asset/IAssetManager.cpp +++ b/src/nbl/asset/IAssetManager.cpp @@ -85,7 +85,6 @@ #include "nbl/asset/interchange/CBufferLoaderBIN.h" #include "nbl/asset/utils/CGeometryCreator.h" -#include "nbl/asset/utils/CMeshManipulator.h" using namespace nbl; @@ -115,29 +114,17 @@ std::function nbl::asset::makeAssetDisposeFunc(const IAsset void IAssetManager::initializeMeshTools() { - m_meshManipulator = core::make_smart_refctd_ptr(); - m_geometryCreator = core::make_smart_refctd_ptr(m_meshManipulator.get()); if (!m_compilerSet) m_compilerSet = core::make_smart_refctd_ptr(core::smart_refctd_ptr(m_system)); } -const IGeometryCreator* IAssetManager::getGeometryCreator() const -{ - return m_geometryCreator.get(); -} - -IMeshManipulator* IAssetManager::getMeshManipulator() -{ - return m_meshManipulator.get(); -} - void IAssetManager::addLoadersAndWriters() { #ifdef _NBL_COMPILE_WITH_STL_LOADER_ addAssetLoader(core::make_smart_refctd_ptr(this)); #endif #ifdef _NBL_COMPILE_WITH_PLY_LOADER_ - addAssetLoader(core::make_smart_refctd_ptr(this)); + addAssetLoader(core::make_smart_refctd_ptr()); #endif #ifdef _NBL_COMPILE_WITH_MTL_LOADER_ addAssetLoader(core::make_smart_refctd_ptr(this, core::smart_refctd_ptr(m_system))); @@ -201,8 +188,6 @@ void IAssetManager::addLoadersAndWriters() SAssetBundle IAssetManager::getAssetInHierarchy_impl(system::IFile* _file, const std::string& _supposedFilename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) { IAssetLoader::SAssetLoadParams params(_params); - if (params.meshManipulatorOverride == nullptr) - params.meshManipulatorOverride = m_meshManipulator.get(); IAssetLoader::SAssetLoadContext ctx{params,_file}; @@ -269,41 +254,6 @@ void IAssetManager::insertBuiltinAssets() insertBuiltinAssetIntoCache(bundle); }; - /* - SBinding for UBO - basic view parameters. - */ - - asset::ICPUDescriptorSetLayout::SBinding binding1; - binding1.count = 1u; - binding1.binding = 0u; - binding1.stageFlags = static_cast(hlsl::ShaderStage::ESS_VERTEX | hlsl::ShaderStage::ESS_FRAGMENT); - binding1.type = asset::IDescriptor::E_TYPE::ET_UNIFORM_BUFFER; - - auto ds1Layout = core::make_smart_refctd_ptr(&binding1, &binding1 + 1); - addBuiltInToCaches(ds1Layout, "nbl/builtin/material/lambertian/singletexture/descriptor_set_layout/1"); - - /* - SBinding for the texture (sampler). - */ - - asset::ICPUDescriptorSetLayout::SBinding binding3; - binding3.binding = 0u; - binding3.type = IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER; - binding3.count = 1u; - binding3.stageFlags = static_cast(hlsl::ShaderStage::ESS_FRAGMENT); - binding3.immutableSamplers = nullptr; - - auto ds3Layout = core::make_smart_refctd_ptr(&binding3, &binding3 + 1); - addBuiltInToCaches(ds3Layout, "nbl/builtin/material/lambertian/singletexture/descriptor_set_layout/3"); // TODO find everything what has been using it so far - - constexpr uint32_t pcCount = 1u; - asset::SPushConstantRange pcRanges[pcCount] = {asset::IShader::E_SHADER_STAGE::ESS_VERTEX,0u,sizeof(core::matrix4SIMD)}; - auto pLayout = core::make_smart_refctd_ptr( - std::span(pcRanges,pcCount), - nullptr,core::smart_refctd_ptr(ds1Layout),nullptr,core::smart_refctd_ptr(ds3Layout) - ); - addBuiltInToCaches(pLayout,"nbl/builtin/material/lambertian/singletexture/pipeline_layout"); // TODO find everything what has been using it so far - // samplers { asset::ISampler::SParams params; @@ -384,57 +334,4 @@ void IAssetManager::insertBuiltinAssets() addBuiltInToCaches(dummy2dImgView, "nbl/builtin/image_view/dummy2d"); addBuiltInToCaches(dummy2dImage, "nbl/builtin/image/dummy2d"); } - - //ds layouts - core::smart_refctd_ptr defaultDs1Layout; - { - asset::ICPUDescriptorSetLayout::SBinding bnd; - bnd.count = 1u; - bnd.binding = 0u; - //maybe even ESS_ALL_GRAPHICS? - bnd.stageFlags = static_cast(hlsl::ShaderStage::ESS_VERTEX | hlsl::ShaderStage::ESS_FRAGMENT); - bnd.type = asset::IDescriptor::E_TYPE::ET_UNIFORM_BUFFER; - defaultDs1Layout = core::make_smart_refctd_ptr(&bnd, &bnd+1); - //it's intentionally added to cache later, see comments below, dont touch this order of insertions - } - - //desc sets - { - auto ds1 = core::make_smart_refctd_ptr(core::smart_refctd_ptr(defaultDs1Layout.get())); - { - constexpr size_t UBO_SZ = sizeof(asset::SBasicViewParameters); - auto ubo = asset::ICPUBuffer::create({ UBO_SZ }); - //for filling this UBO with actual data, one can use asset::SBasicViewParameters struct defined in nbl/asset/asset_utils.h - asset::fillBufferWithDeadBeef(ubo.get()); - - auto descriptorInfos = ds1->getDescriptorInfos(ICPUDescriptorSetLayout::CBindingRedirect::binding_number_t(0), IDescriptor::E_TYPE::ET_UNIFORM_BUFFER); - descriptorInfos.begin()[0].desc = std::move(ubo); - descriptorInfos.begin()[0].info.buffer.offset = 0ull; - descriptorInfos.begin()[0].info.buffer.size = UBO_SZ; - } - addBuiltInToCaches(ds1, "nbl/builtin/descriptor_set/basic_view_parameters"); - addBuiltInToCaches(defaultDs1Layout, "nbl/builtin/descriptor_set_layout/basic_view_parameters"); - } - - // pipeline layout - core::smart_refctd_ptr pipelineLayout; - { - asset::ICPUDescriptorSetLayout::SBinding bnd; - bnd.count = 1u; - bnd.binding = 0u; - bnd.stageFlags = static_cast(hlsl::ShaderStage::ESS_VERTEX | hlsl::ShaderStage::ESS_FRAGMENT); - bnd.type = asset::IDescriptor::E_TYPE::ET_UNIFORM_BUFFER; - auto ds1Layout = core::make_smart_refctd_ptr(&bnd, &bnd + 1); - - pipelineLayout = core::make_smart_refctd_ptr(std::span(),nullptr,std::move(ds1Layout),nullptr,nullptr); - auto paths = - { - "nbl/builtin/material/lambertian/no_texture/pipeline_layout", - "nbl/builtin/pipeline_layout/loader/PLY", - "nbl/builtin/pipeline_layout/loader/STL" - }; - - for(auto &path : paths) - addBuiltInToCaches(pipelineLayout, path); - } } diff --git a/src/nbl/asset/ICPUPolygonGeometry.cpp b/src/nbl/asset/ICPUPolygonGeometry.cpp new file mode 100644 index 0000000000..74970f805b --- /dev/null +++ b/src/nbl/asset/ICPUPolygonGeometry.cpp @@ -0,0 +1,131 @@ +#include "nbl/asset/ICPUPolygonGeometry.h" + +#include + +using namespace nbl; +using namespace asset; + + +template requires (Order>0) +class CListIndexingCB final : public IPolygonGeometryBase::IIndexingCallback +{ + template + static void operator_impl(SContext& ctx) + { + auto indexOfIndex = ctx.beginPrimitive*3; + for (const auto end=ctx.endPrimitive*3; indexOfIndex!=end; indexOfIndex+=3) + ctx.streamOut(indexOfIndex,std::ranges::iota_view{0,int(Order)}); + } + + public: + uint8_t degree_impl() const override {return Order;} + uint8_t rate_impl() const override {return Order;} + void operator()(SContext& ctx) const override {operator_impl(ctx);} + void operator()(SContext& ctx) const override {operator_impl(ctx);} + void operator()(SContext& ctx) const override {operator_impl(ctx);} + + E_PRIMITIVE_TOPOLOGY knownTopology() const override + { + switch (Order) + { + case 1: + return EPT_POINT_LIST; + case 2: + return EPT_LINE_LIST; + case 3: + return EPT_TRIANGLE_LIST; + default: + return EPT_PATCH_LIST; + } + } +}; +auto IPolygonGeometryBase::PointList() -> IIndexingCallback* +{ + static CListIndexingCB<1> singleton; + return &singleton; +} +auto IPolygonGeometryBase::LineList() -> IIndexingCallback* +{ + static CListIndexingCB<2> singleton; + return &singleton; +} +auto IPolygonGeometryBase::TriangleList() -> IIndexingCallback* +{ + static CListIndexingCB<3> singleton; + return &singleton; +} +auto IPolygonGeometryBase::QuadList() -> IIndexingCallback* +{ + static CListIndexingCB<4> singleton; + return &singleton; +} + +class CTriangleStripIndexingCB final : public IPolygonGeometryBase::IIndexingCallback +{ + template + static void operator_impl(SContext& ctx) + { + uint64_t indexOfIndex; + if (ctx.beginPrimitive==0) + { + ctx.streamOut(0,std::ranges::iota_view{0,3}); + indexOfIndex = 3; + } + else + indexOfIndex = ctx.beginPrimitive+2; + const int32_t perm[] = {-1,-2,0}; + for (const auto end=ctx.endPrimitive+2; indexOfIndex!=end; indexOfIndex++) + ctx.streamOut(indexOfIndex,perm); + } + + public: + inline uint8_t degree_impl() const override { return 3; } + inline uint8_t rate_impl() const override { return 1; } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + + E_PRIMITIVE_TOPOLOGY knownTopology() const override {return EPT_TRIANGLE_STRIP;} +}; +auto IPolygonGeometryBase::TriangleStrip() -> IIndexingCallback* +{ + static CTriangleStripIndexingCB singleton; + return &singleton; +} + +class CTriangleFanIndexingCB final : public IPolygonGeometryBase::IIndexingCallback +{ + template + static void operator_impl(SContext& ctx) + { + uint64_t indexOfIndex; + if (ctx.beginPrimitive==0) + { + ctx.streamOut(0,std::ranges::iota_view{0,3}); + indexOfIndex = 3; + } + else + indexOfIndex = ctx.beginPrimitive+2; + int32_t perm[] = {0x7eadbeefu,-1,0}; + for (const auto end=ctx.endPrimitive+2; indexOfIndex!=end; indexOfIndex++) + { + // first index is always global 0 + perm[0] = -indexOfIndex; + ctx.streamOut(indexOfIndex,perm); + } + } + + public: + inline uint8_t degree_impl() const override {return 3;} + inline uint8_t rate_impl() const override {return 1;} + void operator()(SContext& ctx) const override { operator_impl(ctx); } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + void operator()(SContext& ctx) const override { operator_impl(ctx); } + + E_PRIMITIVE_TOPOLOGY knownTopology() const override {return EPT_TRIANGLE_FAN;} +}; +auto IPolygonGeometryBase::TriangleFan() -> IIndexingCallback* +{ + static CTriangleFanIndexingCB singleton; + return &singleton; +} \ No newline at end of file diff --git a/src/nbl/asset/interchange/CBufferLoaderBIN.h b/src/nbl/asset/interchange/CBufferLoaderBIN.h index 2d0ea5f765..b47866ff3c 100644 --- a/src/nbl/asset/interchange/CBufferLoaderBIN.h +++ b/src/nbl/asset/interchange/CBufferLoaderBIN.h @@ -1,16 +1,13 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_BUFFER_LOADER_H_INCLUDED__ -#define __NBL_ASSET_C_BUFFER_LOADER_H_INCLUDED__ +#ifndef _NBL_ASSET_C_BUFFER_LOADER_H_INCLUDED_ +#define _NBL_ASSET_C_BUFFER_LOADER_H_INCLUDED_ #include "nbl/asset/interchange/IAssetLoader.h" -#include "nbl/asset/ICPUMeshBuffer.h" +#include "nbl/asset/ICPUBuffer.h" -namespace nbl -{ -namespace asset +namespace nbl::asset { //! Binaryloader capable of loading source code in binary format @@ -43,6 +40,4 @@ class CBufferLoaderBIN final : public asset::IAssetLoader }; } -} - #endif diff --git a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp index 8d6f575ae5..d4b9a3e394 100644 --- a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp +++ b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h @@ -15,17 +15,11 @@ #include "nbl/builtin/MTLdefaults.h" - using namespace nbl; using namespace asset; -#define VERT_SHADER_NO_UV_CACHE_KEY "nbl/builtin/shader/loader/mtl/vertex_no_uv.vert" -#define VERT_SHADER_UV_CACHE_KEY "nbl/builtin/shader/loader/mtl/vertex_uv.vert" -#define FRAG_SHADER_NO_UV_CACHE_KEY "nbl/builtin/shader/loader/mtl/fragment_no_uv.frag" -#define FRAG_SHADER_UV_CACHE_KEY "nbl/builtin/shader/loader/mtl/fragment_uv.frag" -CGraphicsPipelineLoaderMTL::CGraphicsPipelineLoaderMTL(IAssetManager* _am, core::smart_refctd_ptr&& sys) : - IRenderpassIndependentPipelineLoader(_am), m_system(std::move(sys)) +CGraphicsPipelineLoaderMTL::CGraphicsPipelineLoaderMTL(IAssetManager* _am, core::smart_refctd_ptr&& sys) : m_system(std::move(sys)) { #if 0 // Remove IRenderpassIndependentPipelines and use MC for Mesh Loaders //create vertex shaders and insert them into cache @@ -70,6 +64,7 @@ CGraphicsPipelineLoaderMTL::CGraphicsPipelineLoaderMTL(IAssetManager* _am, core: #endif } +#if 0 void CGraphicsPipelineLoaderMTL::initialize() { IRenderpassIndependentPipelineLoader::initialize(); @@ -123,6 +118,7 @@ void CGraphicsPipelineLoaderMTL::initialize() insertBuiltinAssetIntoCache(m_assetMgr, bundle, "nbl/builtin/renderpass_independent_pipeline/loader/mtl/missing_material_pipeline"); } +#endif bool CGraphicsPipelineLoaderMTL::isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const { @@ -138,6 +134,8 @@ bool CGraphicsPipelineLoaderMTL::isALoadableFileFormat(system::IFile* _file, con SAssetBundle CGraphicsPipelineLoaderMTL::loadAsset(system::IFile* _file, const IAssetLoader::SAssetLoadParams& _params, IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel) { + return {}; +#if 0 // Remove IRenderpassIndependentPipelines and use MC for Mesh Loaders SContext ctx( asset::IAssetLoader::SAssetLoadContext{ _params, @@ -196,12 +194,12 @@ SAssetBundle CGraphicsPipelineLoaderMTL::loadAsset(system::IFile* _file, const I if (materials.empty()) return SAssetBundle(nullptr, {}); return SAssetBundle(std::move(meta),std::move(retval)); +#endif } +#if 0 // Remove IRenderpassIndependentPipelines and use MC for Mesh Loaders core::smart_refctd_ptr CGraphicsPipelineLoaderMTL::makePipelineFromMtl(SContext& _ctx, const SMtl& _mtl, bool hasUV) { - return nullptr; -#if 0 // Remove IRenderpassIndependentPipelines and use MC for Mesh Loaders SBlendParams blendParams; std::string cacheKey("nbl/builtin/renderpass_independent_pipeline/loader/mtl/"); @@ -323,8 +321,8 @@ core::smart_refctd_ptr CGraphicsPipelineLoade ppln = core::make_smart_refctd_ptr(std::move(layout), shaders, shaders+2u, vtxParams, blendParams, SPrimitiveAssemblyParams{}, SRasterizationParams{}); } return ppln; -#endif } +#endif namespace { @@ -403,6 +401,7 @@ namespace } } +#if 0 const char* CGraphicsPipelineLoaderMTL::readTexture(const char* _bufPtr, const char* const _bufEnd, SMtl* _currMaterial, const char* _mapType) const { static const std::unordered_map str2type = @@ -912,3 +911,4 @@ auto CGraphicsPipelineLoaderMTL::readMaterials(system::IFile* _file, const syste return materials; } +#endif diff --git a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.h b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.h index 3eb4ffea51..b95632f269 100644 --- a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.h +++ b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.h @@ -1,18 +1,20 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_GRAPHICS_PIPELINE_LOADER_MTL_H_INCLUDED_ +#define _NBL_ASSET_C_GRAPHICS_PIPELINE_LOADER_MTL_H_INCLUDED_ -#ifndef __NBL_ASSET_C_GRAPHICS_PIPELINE_LOADER_MTL_H_INCLUDED__ -#define __NBL_ASSET_C_GRAPHICS_PIPELINE_LOADER_MTL_H_INCLUDED__ -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" +#include "nbl/asset/interchange/IGeometryLoader.h" #include "nbl/asset/metadata/CMTLMetadata.h" + namespace nbl::asset { -class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPipelineLoader +class CGraphicsPipelineLoaderMTL final : public IAssetLoader // TODO: Material Asset and Loader { +#if 0 struct SMtl { CMTLMetadata::CRenderpassIndependentPipeline::SMaterialParameters params; @@ -25,7 +27,7 @@ class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPip inline bool isClampToBorder(CMTLMetadata::CRenderpassIndependentPipeline::E_MAP_TYPE m) const { return (clamp >> m) & 1u; } }; - +#endif struct SContext { SContext(const IAssetLoader::SAssetLoadContext& _innerCtx, uint32_t _topHierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) @@ -39,8 +41,6 @@ class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPip public: CGraphicsPipelineLoaderMTL(IAssetManager* _am, core::smart_refctd_ptr&& sys); - void initialize() override; - bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger=nullptr) const override; const char** getAssociatedFileExtensions() const override @@ -49,11 +49,12 @@ class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPip return extensions; } - uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_RENDERPASS_INDEPENDENT_PIPELINE; } +// uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MATERIAL; } asset::SAssetBundle loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel = 0u) override; private: +#if 0 core::smart_refctd_ptr makePipelineFromMtl(SContext& ctx, const SMtl& _mtl, bool hasUV); core::vector readMaterials(system::IFile* _file, const system::logger_opt_ptr logger) const; const char* readTexture(const char* _bufPtr, const char* const _bufEnd, SMtl* _currMaterial, const char* _mapType) const; @@ -62,7 +63,8 @@ class CGraphicsPipelineLoaderMTL final : public asset::IRenderpassIndependentPip using image_views_set_t = std::array, CMTLMetadata::CRenderpassIndependentPipeline::EMP_REFL_POSX + 1u>; image_views_set_t loadImages(const std::string& relDir, SMtl& _mtl, SContext& _ctx); core::smart_refctd_ptr makeDescSet(image_views_set_t&& _views, ICPUDescriptorSetLayout* _dsLayout, SContext& _ctx); - private: +#endif + core::smart_refctd_ptr m_system; }; diff --git a/src/nbl/asset/interchange/COBJMeshFileLoader.cpp b/src/nbl/asset/interchange/COBJMeshFileLoader.cpp index 61d9eab6ec..69651f8061 100644 --- a/src/nbl/asset/interchange/COBJMeshFileLoader.cpp +++ b/src/nbl/asset/interchange/COBJMeshFileLoader.cpp @@ -6,14 +6,13 @@ #include "nbl/core/declarations.h" #include "nbl/asset/IAssetManager.h" -#include "nbl/asset/utils/IMeshManipulator.h" +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" #ifdef _NBL_COMPILE_WITH_OBJ_LOADER_ #include "nbl/system/ISystem.h" #include "nbl/system/IFile.h" -#include "nbl/asset/metadata/COBJMetadata.h" #include "nbl/asset/utils/CQuantNormalCache.h" #include "COBJMeshFileLoader.h" diff --git a/src/nbl/asset/interchange/COBJMeshFileLoader.h b/src/nbl/asset/interchange/COBJMeshFileLoader.h index 47d7683997..c11a09e671 100644 --- a/src/nbl/asset/interchange/COBJMeshFileLoader.h +++ b/src/nbl/asset/interchange/COBJMeshFileLoader.h @@ -1,13 +1,12 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors - -#ifndef __NBL_ASSET_C_OBJ_MESH_FILE_LOADER_H_INCLUDED__ -#define __NBL_ASSET_C_OBJ_MESH_FILE_LOADER_H_INCLUDED__ +#ifndef _NBL_ASSET_C_OBJ_MESH_FILE_LOADER_H_INCLUDED_ +#define _NBL_ASSET_C_OBJ_MESH_FILE_LOADER_H_INCLUDED_ #include "nbl/core/declarations.h" -#include "nbl/asset/ICPUMeshBuffer.h" +#include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/interchange/IAssetLoader.h" #include "nbl/asset/metadata/CMTLMetadata.h" @@ -53,7 +52,7 @@ class SObjVertex #include "nbl/nblunpack.h" //! Meshloader capable of loading obj meshes. -class COBJMeshFileLoader : public asset::IAssetLoader +class COBJMeshFileLoader : public IGeometryLoader { struct SContext { @@ -91,8 +90,6 @@ class COBJMeshFileLoader : public asset::IAssetLoader return ext; } - virtual uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MESH; } - virtual asset::SAssetBundle loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; private: diff --git a/src/nbl/asset/interchange/CPLYMeshFileLoader.cpp b/src/nbl/asset/interchange/CPLYMeshFileLoader.cpp index 3cdc261408..c60412eaf2 100644 --- a/src/nbl/asset/interchange/CPLYMeshFileLoader.cpp +++ b/src/nbl/asset/interchange/CPLYMeshFileLoader.cpp @@ -2,1164 +2,908 @@ // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors +#ifdef _NBL_COMPILE_WITH_PLY_LOADER_ -#include "CPLYMeshFileLoader.h" -#ifdef _NBL_COMPILE_WITH_PLY_LOADER_ +#include "CPLYMeshFileLoader.h" #include #include "nbl/asset/IAssetManager.h" + #include "nbl/system/ISystem.h" #include "nbl/system/IFile.h" -#include "nbl/asset/utils/IMeshManipulator.h" - -namespace nbl -{ -namespace asset -{ -CPLYMeshFileLoader::CPLYMeshFileLoader(IAssetManager* _am) - : IRenderpassIndependentPipelineLoader(_am) -{ +//#include "nbl/asset/utils/IMeshManipulator.h" -} -CPLYMeshFileLoader::~CPLYMeshFileLoader() {} +namespace nbl::asset +{ bool CPLYMeshFileLoader::isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const { - const char* headers[3]{ - "format ascii 1.0", - "format binary_little_endian 1.0", - "format binary_big_endian 1.0" - }; - char buf[40]; system::IFile::success_t success; - _file->read(success, buf, 0, sizeof(buf)); + _file->read(success,buf,0,sizeof(buf)); if (!success) return false; char* header = buf; - if (strncmp(header, "ply", 3u) != 0) + if (strncmp(header,"ply",3u)!=0) return false; header += 4; - char* lf = strstr(header, "\n"); + char* lf = strstr(header,"\n"); if (!lf) return false; - *lf = 0; - - for (uint32_t i = 0u; i < 3u; ++i) - if (strcmp(header, headers[i]) == 0) - return true; - return false; + + constexpr std::array headers = { + "format ascii 1.0", + "format binary_little_endian 1.0", + "format binary_big_endian 1.0" + }; + return std::find(headers.begin(),headers.end(),std::string_view(header,lf))!=headers.end(); } -void CPLYMeshFileLoader::initialize() +template +inline T byteswap(const T& v) { - IRenderpassIndependentPipelineLoader::initialize(); + T retval; + auto it = reinterpret_cast(&v); + std::reverse_copy(it,it+sizeof(T),reinterpret_cast(&retval)); + return retval; +} - auto precomputeAndCachePipeline = [&](CPLYMeshFileLoader::E_TYPE type, bool indexBufferBindingAvailable) +struct SContext +{ + + // + struct SProperty { - constexpr std::array>, 3> avaiableOptionsForShaders - { - std::make_pair(ET_COL, std::make_pair("nbl/builtin/material/debug/vertex_color/specialized_shader.vert", "nbl/builtin/material/debug/vertex_color/specialized_shader.frag")), - std::make_pair(ET_UV, std::make_pair("nbl/builtin/material/debug/vertex_uv/specialized_shader.vert", "nbl/builtin/material/debug/vertex_uv/specialized_shader.frag")), - std::make_pair(ET_NORM, std::make_pair("nbl/builtin/material/debug/vertex_normal/specialized_shader.vert", "nbl/builtin/material/debug/vertex_normal/specialized_shader.frag")) - }; - - auto chooseShaderPaths = [&]() -> std::pair + static E_FORMAT getType(const char* typeString) { - switch (type) - { - case ET_POS: - case ET_COL: - return std::make_pair("nbl/builtin/material/debug/vertex_color/specialized_shader.vert", "nbl/builtin/material/debug/vertex_color/specialized_shader.frag"); - case ET_UV: - return std::make_pair("nbl/builtin/material/debug/vertex_uv/specialized_shader.vert", "nbl/builtin/material/debug/vertex_uv/specialized_shader.frag"); - case ET_NORM: - return std::make_pair("nbl/builtin/material/debug/vertex_normal/specialized_shader.vert", "nbl/builtin/material/debug/vertex_normal/specialized_shader.frag"); - default: - return {}; - } - }; + if (strcmp(typeString, "char")==0 || strcmp(typeString, "int8")==0) + return EF_R8_SINT; + else if (strcmp(typeString, "uchar")==0 || strcmp(typeString, "uint8")==0) + return EF_R8_UINT; + else if (strcmp(typeString, "short")==0 || strcmp(typeString, "int16")==0) + return EF_R16_SINT; + else if (strcmp(typeString, "ushort")==0 || strcmp(typeString, "uint16")==0) + return EF_R16_UINT; + else if (strcmp(typeString, "long")==0 || strcmp(typeString, "int")==0 || strcmp(typeString, "int16")==0) + return EF_R32_SINT; + else if (strcmp(typeString, "ulong")==0 || strcmp(typeString, "uint16")==0) + return EF_R32_UINT; + else if (strcmp(typeString, "float")==0 || strcmp(typeString, "float32")==0) + return EF_R32_SFLOAT; + else if (strcmp(typeString, "double")==0 || strcmp(typeString, "float64")==0) + return EF_R64_SFLOAT; + else + return EF_UNKNOWN; + } - auto defaultOverride = IAssetLoaderOverride(m_assetMgr); - const std::string pipelineCacheHash = getPipelineCacheKey(type, indexBufferBindingAvailable); - const uint32_t _hierarchyLevel = 0; - const IAssetLoader::SAssetLoadContext fakeContext(IAssetLoader::SAssetLoadParams{}, nullptr); + inline bool isList() const {return type==EF_UNKNOWN && asset::isIntegerFormat(list.countType) && asset::isIntegerFormat(list.itemType);} - const asset::IAsset::E_TYPE types[]{ asset::IAsset::ET_RENDERPASS_INDEPENDENT_PIPELINE, (asset::IAsset::E_TYPE)0u }; - auto pipelineBundle = defaultOverride.findCachedAsset(pipelineCacheHash, types, fakeContext, _hierarchyLevel + ICPURenderpassIndependentPipeline::DESC_SET_HIERARCHYLEVELS_BELOW); - if (pipelineBundle.getContents().empty()) + void skip(SContext& _ctx) const { - auto mbVertexShader = core::smart_refctd_ptr(); - auto mbFragmentShader = core::smart_refctd_ptr(); - { - const IAsset::E_TYPE types[]{ IAsset::E_TYPE::ET_SPECIALIZED_SHADER, static_cast(0u) }; - const auto shaderPaths = chooseShaderPaths(); - - auto vertexShaderBundle = m_assetMgr->findAssets(shaderPaths.first.data(), types); - auto fragmentShaderBundle = m_assetMgr->findAssets(shaderPaths.second.data(), types); - - mbVertexShader = core::smart_refctd_ptr_static_cast(vertexShaderBundle->begin()->getContents().begin()[0]); - mbFragmentShader = core::smart_refctd_ptr_static_cast(fragmentShaderBundle->begin()->getContents().begin()[0]); - } - - auto mbPipelineLayout = defaultOverride.findDefaultAsset("nbl/builtin/pipeline_layout/loader/PLY", fakeContext, 0u).first; - - const std::array vertexAttribParamsAllOptions = + if (isList()) { - SVertexInputAttribParams(0u, EF_R32G32B32_SFLOAT, 0), - SVertexInputAttribParams(1u, EF_R32G32B32A32_SFLOAT, 0), - SVertexInputAttribParams(2u, EF_R32G32_SFLOAT, 0), - SVertexInputAttribParams(3u, EF_R32G32B32_SFLOAT, 0) - }; - - SVertexInputParams inputParams; - - std::vector availableAttributes = { ET_POS }; - if (type != ET_POS) - availableAttributes.push_back(static_cast(type)); + int32_t count = _ctx.getInt(list.countType); - for (auto& attrib : availableAttributes) - { - const auto currentBitmask = core::createBitmask({ attrib }); - inputParams.enabledBindingFlags |= currentBitmask; - inputParams.enabledAttribFlags |= currentBitmask; - inputParams.bindings[attrib] = { asset::getTexelOrBlockBytesize(static_cast(vertexAttribParamsAllOptions[attrib].format)), EVIR_PER_VERTEX }; - inputParams.attributes[attrib] = vertexAttribParamsAllOptions[attrib]; + for (decltype(count) i=0; i(std::move(mbPipelineLayout), nullptr, nullptr, inputParams, blendParams, primitiveAssemblyParams, rastarizationParmas); - { - mbPipeline->setShaderAtStage(asset::IShader::ESS_VERTEX, mbVertexShader.get()); - mbPipeline->setShaderAtStage(asset::IShader::ESS_FRAGMENT, mbFragmentShader.get()); - - asset::SAssetBundle newPipelineBundle(nullptr, { core::smart_refctd_ptr(mbPipeline) }); - defaultOverride.insertAssetIntoCache(newPipelineBundle, pipelineCacheHash, fakeContext, _hierarchyLevel + ICPURenderpassIndependentPipeline::DESC_SET_HIERARCHYLEVELS_BELOW); - } + _ctx.getNextWord(); } - else - return; - }; - - /* - Pipeline permutations are cached - */ - - precomputeAndCachePipeline(ET_POS, false); - precomputeAndCachePipeline(ET_COL, false); - precomputeAndCachePipeline(ET_UV, false); - precomputeAndCachePipeline(ET_NORM, false); - - precomputeAndCachePipeline(ET_POS, true); - precomputeAndCachePipeline(ET_COL, true); - precomputeAndCachePipeline(ET_UV, true); - precomputeAndCachePipeline(ET_NORM, true); -} - -//! creates/loads an animated mesh from the file. -asset::SAssetBundle CPLYMeshFileLoader::loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel) -{ - if (!_file) - return {}; - SContext ctx = { - asset::IAssetLoader::SAssetLoadContext{ - _params, - _file - }, - _hierarchyLevel, - _override + std::string Name; + E_FORMAT type; + struct SListTypes + { + E_FORMAT countType; + E_FORMAT itemType; + } list; }; - - // attempt to allocate the buffer and fill with data - if (!allocateBuffer(ctx)) - { - return {}; - } - - const uint32_t WORD_BUFFER_LENGTH = 512u; - char logInputsBuffer[WORD_BUFFER_LENGTH] {}; - - // start with empty mesh - core::smart_refctd_ptr mesh; - uint32_t vertCount=0; - - // Currently only supports ASCII meshes - if (strcmp(getNextLine(ctx), "ply")) - _params.logger.log("Not a valid PLY file %s", system::ILogger::ELL_ERROR, ctx.inner.mainFile->getFileName().string().c_str()); - else + struct SElement { - // cut the next line out - getNextLine(ctx); - // grab the word from this line - char* word = getNextWord(ctx); - - // ignore comments - while (strcmp(word, "comment") == 0) - { - getNextLine(ctx); - word = getNextWord(ctx); - } - - bool readingHeader = true; - bool continueReading = true; - ctx.IsBinaryFile = false; - ctx.IsWrongEndian= false; - - do + void skipElement(SContext& _ctx) const { - if (strcmp(word, "format") == 0) + if (_ctx.IsBinaryFile) { - word = getNextWord(ctx); - - if (strcmp(word, "binary_little_endian") == 0) - { - ctx.IsBinaryFile = true; - } - else if (strcmp(word, "binary_big_endian") == 0) - { - ctx.IsBinaryFile = true; - ctx.IsWrongEndian = true; - } - else if (strcmp(word, "ascii")) - { - // abort if this isn't an ascii or a binary mesh - _params.logger.log("Unsupported PLY mesh format %s", system::ILogger::ELL_ERROR, word); - continueReading = false; - } - - if (continueReading) - { - word = getNextWord(ctx); - if (strcmp(word, "1.0")) - { - _params.logger.log("Unsupported PLY mesh version %s", system::ILogger::ELL_WARNING, word); - } - } - } - else if (strcmp(word, "property") == 0) - { - word = getNextWord(ctx); - - if (!ctx.ElementList.size()) - { - _params.logger.log("PLY property found before element %s", system::ILogger::ELL_WARNING, word); - } + if (KnownSize) + _ctx.moveForward(KnownSize); else - { - // get element - SPLYElement* el = ctx.ElementList.back().get(); - - // fill property struct - SPLYProperty prop; - prop.Type = getPropertyType(word); - el->KnownSize += prop.size(); - - if (prop.Type == EPLYPT_LIST) - { - el->IsFixedWidth = false; - - word = getNextWord(ctx); - - prop.Data.List.CountType = getPropertyType(word); - if (ctx.IsBinaryFile && prop.Data.List.CountType == EPLYPT_UNKNOWN) - { - _params.logger.log("Cannot read binary PLY file containing data types of unknown length %s", system::ILogger::ELL_WARNING, word); - continueReading = false; - } - else - { - word = getNextWord(ctx); - prop.Data.List.ItemType = getPropertyType(word); - if (ctx.IsBinaryFile && prop.Data.List.ItemType == EPLYPT_UNKNOWN) - { - _params.logger.log("Cannot read binary PLY file containing data types of unknown length %s", system::ILogger::ELL_ERROR, word); - continueReading = false; - } - } - } - else if (ctx.IsBinaryFile && prop.Type == EPLYPT_UNKNOWN) - { - _params.logger.log("Cannot read binary PLY file containing data types of unknown length %s", system::ILogger::ELL_ERROR, word); - continueReading = false; - } - - prop.Name = getNextWord(ctx); - - // add property to element - el->Properties.push_back(prop); - } - } - else if (strcmp(word, "element") == 0) - { - auto el = std::make_unique(); - el->Name = getNextWord(ctx); - el->Count = atoi(getNextWord(ctx)); - el->IsFixedWidth = true; - el->KnownSize = 0; - if (el->Name == "vertex") - vertCount = el->Count; - - ctx.ElementList.emplace_back(std::move(el)); - - } - else if (strcmp(word, "end_header") == 0) - { - readingHeader = false; - if (ctx.IsBinaryFile) - { - ctx.StartPointer = ctx.LineEndPointer + 1; - } - } - else if (strcmp(word, "comment") == 0) - { - // ignore line + for (auto i=0u; i(); - - mb->setNormalAttributeIx(3u); - - asset::SBufferBinding attributes[4]; - core::vector indices; - - bool hasNormals = true; - - // loop through each of the elements - for (uint32_t i=0; iName == "vertex") - { - auto& plyVertexElement = *ctx.ElementList[i]; - - for (auto& vertexProperty : plyVertexElement.Properties) - { - const auto propertyName = vertexProperty.Name; - - if (propertyName == "x" || propertyName == "y" || propertyName == "z") - { - if (!attributes[ET_POS].buffer) - { - attributes[ET_POS].offset = 0u; - attributes[ET_POS].buffer = asset::ICPUBuffer::create({ asset::getTexelOrBlockBytesize(EF_R32G32B32_SFLOAT) * plyVertexElement.Count }); - } - } - else if(propertyName == "nx" || propertyName == "ny" || propertyName == "nz") - { - if (!attributes[ET_NORM].buffer) - { - attributes[ET_NORM].offset = 0u; - attributes[ET_NORM].buffer = asset::ICPUBuffer::create({ asset::getTexelOrBlockBytesize(EF_R32G32B32_SFLOAT) * plyVertexElement.Count }); - } - } - else if (propertyName == "u" || propertyName == "s" || propertyName == "v" || propertyName == "t") - { - if (!attributes[ET_UV].buffer) - { - attributes[ET_UV].offset = 0u; - attributes[ET_UV].buffer = asset::ICPUBuffer::create({ asset::getTexelOrBlockBytesize(EF_R32G32_SFLOAT) * plyVertexElement.Count }); - } - } - else if (propertyName == "red" || propertyName == "green" || propertyName == "blue" || propertyName == "alpha") - { - if (!attributes[ET_COL].buffer) - { - attributes[ET_COL].offset = 0u; - attributes[ET_COL].buffer = asset::ICPUBuffer::create({ asset::getTexelOrBlockBytesize(EF_R32G32B32A32_SFLOAT) * plyVertexElement.Count }); - } - } - } - - // loop through vertex properties - for (uint32_t j=0; jCount; ++j) - hasNormals &= readVertex(ctx, plyVertexElement, attributes, j, _params); - } - else if (ctx.ElementList[i]->Name == "face") - { - const size_t indicesCount = ctx.ElementList[i]->Count; - - // read faces - for (uint32_t j=0; j < indicesCount; ++j) - readFace(ctx, *ctx.ElementList[i], indices); - } - else - { - // skip these elements - for (uint32_t j=0; j < ctx.ElementList[i]->Count; ++j) - skipElement(ctx, *ctx.ElementList[i]); - } - } - - mb->setPositionAttributeIx(0); - - if (indices.size()) - { - asset::SBufferBinding indexBinding = { 0, asset::ICPUBuffer::create({ indices.size() * sizeof(uint32_t) }) }; - memcpy(indexBinding.buffer->getPointer(), indices.data(), indexBinding.buffer->getSize()); - - mb->setIndexCount(indices.size()); - mb->setIndexBufferBinding(std::move(indexBinding)); - mb->setIndexType(asset::EIT_32BIT); - - if (!genVertBuffersForMBuffer(mb.get(), attributes, ctx)) - return {}; - } - else - { - mb->setIndexCount(attributes[ET_POS].buffer->getSize()); - mb->setIndexType(EIT_UNKNOWN); - if (!genVertBuffersForMBuffer(mb.get(), attributes, ctx)) - return {}; - } - - IMeshManipulator::recalculateBoundingBox(mb.get()); + // name of the element. We only want "vertex" and "face" elements + // but we have to parse the others anyway. + std::string Name; + // Properties of this element + core::vector Properties; + // The number of elements in the file + size_t Count; + // known size in bytes, 0 if unknown + uint32_t KnownSize; + }; - mesh = core::make_smart_refctd_ptr(); - mesh->getMeshBufferVector().emplace_back(std::move(mb)); + inline void init() + { + EndPointer = StartPointer = Buffer.data(); + LineEndPointer = EndPointer-1; - IMeshManipulator::recalculateBoundingBox(mesh.get()); - } + fillBuffer(); } - - auto* mbPipeline = mesh->getMeshBuffers().begin()[0]->getPipeline(); - auto meta = core::make_smart_refctd_ptr(1u, std::move(m_basicViewParamsSemantics)); - meta->placeMeta(0u, mbPipeline); - - return SAssetBundle(std::move(meta),{ std::move(mesh) }); -} - -static void performActionBasedOnOrientationSystem(const asset::IAssetLoader::SAssetLoadParams& _params, std::function performOnRightHanded, std::function performOnLeftHanded) -{ - if (_params.loaderFlags & IAssetLoader::ELPF_RIGHT_HANDED_MESHES) - performOnRightHanded(); - else - performOnLeftHanded(); -} - -bool CPLYMeshFileLoader::readVertex(SContext& _ctx, const SPLYElement& Element, asset::SBufferBinding outAttributes[4], const uint32_t& currentVertexIndex, const asset::IAssetLoader::SAssetLoadParams& _params) -{ - if (!_ctx.IsBinaryFile) - getNextLine(_ctx); - std::pair attribs[4]; - attribs[ET_COL].second.W = 1.f; - attribs[ET_NORM].second.Y = 1.f; - - constexpr auto ET_POS_BYTESIZE = asset::getTexelOrBlockBytesize(); - constexpr auto ET_NORM_BYTESIZE = asset::getTexelOrBlockBytesize(); - constexpr auto ET_UV_BYTESIZE = asset::getTexelOrBlockBytesize(); - constexpr auto ET_COL_BYTESIZE = asset::getTexelOrBlockBytesize(); - - bool result = false; - for (uint32_t i = 0; i < Element.Properties.size(); ++i) + // gets more data from the file + void fillBuffer() { - E_PLY_PROPERTY_TYPE t = Element.Properties[i].Type; - - if (Element.Properties[i].Name == "x") - { - auto& value = attribs[ET_POS].second.X = getFloat(_ctx, t); - attribs[ET_POS].first = true; - - if (_params.loaderFlags & E_LOADER_PARAMETER_FLAGS::ELPF_RIGHT_HANDED_MESHES) - performActionBasedOnOrientationSystem(value, [](float& varToFlip) { varToFlip = -varToFlip; }); - - const size_t propertyOffset = ET_POS_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_POS].buffer->getPointer()) + propertyOffset; - - reinterpret_cast(data)[0] = value; - } - else if (Element.Properties[i].Name == "y") + if (EndOfFile) + return; + else if (fileOffset>=inner.mainFile->getSize()) { - auto& value = attribs[ET_POS].second.Y = getFloat(_ctx, t); - attribs[ET_POS].first = true; - - const size_t propertyOffset = ET_POS_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_POS].buffer->getPointer()) + propertyOffset; - - reinterpret_cast(data)[1] = value; + EndOfFile = true; + return; } - else if (Element.Properties[i].Name == "z") - { - auto& value = attribs[ET_POS].second.Z = getFloat(_ctx, t); - attribs[ET_POS].first = true; + + const auto length = std::distance(StartPointer,EndPointer); + auto newStart = Buffer.data(); + // copy the remaining data to the start of the buffer + if (length && StartPointer!=newStart) + memmove(newStart,StartPointer,length); + // reset start position + StartPointer = newStart; + EndPointer = newStart+length; - const size_t propertyOffset = ET_POS_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_POS].buffer->getPointer()) + propertyOffset; + // read data from the file + const size_t requestSize = Buffer.size()-length; + system::IFile::success_t success; + inner.mainFile->read(success,EndPointer,fileOffset,requestSize); + const size_t bytesRead = success.getBytesProcessed(); + fileOffset += bytesRead; + EndPointer += bytesRead; - reinterpret_cast(data)[2] = value; - } - else if (Element.Properties[i].Name == "nx") + // if we didn't completely fill the buffer + if (bytesRead!=requestSize) { - auto& value = attribs[ET_NORM].second.X = getFloat(_ctx, t); - attribs[ET_NORM].first = result = true; - - if (_params.loaderFlags & E_LOADER_PARAMETER_FLAGS::ELPF_RIGHT_HANDED_MESHES) - performActionBasedOnOrientationSystem(attribs[ET_NORM].second.X, [](float& varToFlip) { varToFlip = -varToFlip; }); - - const size_t propertyOffset = ET_NORM_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_NORM].buffer->getPointer()) + propertyOffset; - - reinterpret_cast(data)[0] = value; + // cauterize the string + *EndPointer = 0; + EndOfFile = true; } - else if (Element.Properties[i].Name == "ny") - { - auto& value = attribs[ET_NORM].second.Y = getFloat(_ctx, t); - attribs[ET_NORM].first = result = true; - - const size_t propertyOffset = ET_NORM_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_NORM].buffer->getPointer()) + propertyOffset; + } + // Split the string data into a line in place by terminating it instead of copying. + const char* getNextLine() + { + // move the start pointer along + StartPointer = LineEndPointer+1; - reinterpret_cast(data)[1] = value; - } - else if (Element.Properties[i].Name == "nz") - { - auto& value = attribs[ET_NORM].second.Z = getFloat(_ctx, t); - attribs[ET_NORM].first = result = true; + // crlf split across buffer move + if (*StartPointer=='\n') + *(StartPointer++) = '\0'; - const size_t propertyOffset = ET_NORM_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_NORM].buffer->getPointer()) + propertyOffset; + // begin at the start of the next line + const std::array Terminators = { '\0','\r','\n'}; + auto terminator = std::find_first_of(StartPointer,EndPointer,Terminators.begin(),Terminators.end()); + if (terminator!=EndPointer) + *(terminator++) = '\0'; - reinterpret_cast(data)[2] = value; - } - // there isn't a single convention for the UV, some softwares like Blender or Assimp use "st" instead of "uv" - else if (Element.Properties[i].Name == "u" || Element.Properties[i].Name == "s") + // we have reached the end of the buffer + if (terminator==EndPointer) { - auto& value = attribs[ET_UV].second.X = getFloat(_ctx, t); - attribs[ET_UV].first = true; - - const size_t propertyOffset = ET_UV_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_UV].buffer->getPointer()) + propertyOffset; - - reinterpret_cast(data)[0] = value; + // get data from the file + if (EndOfFile) + { + StartPointer = EndPointer-1; + *StartPointer = '\0'; + return StartPointer; + } + else + { + fillBuffer(); + // reset line end pointer + LineEndPointer = StartPointer-1; + if (StartPointer!=EndPointer) + return getNextLine(); + else + return StartPointer; + } } - else if (Element.Properties[i].Name == "v" || Element.Properties[i].Name == "t") + else { - auto& value = attribs[ET_UV].second.Y = getFloat(_ctx, t); - attribs[ET_UV].first = true; - - const size_t propertyOffset = ET_UV_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_UV].buffer->getPointer()) + propertyOffset; - - reinterpret_cast(data)[1] = value; + LineEndPointer = terminator-1; + WordLength = -1; + // return pointer to the start of the line + return StartPointer; } - else if (Element.Properties[i].Name == "red") - { - float value = Element.Properties[i].isFloat() ? getFloat(_ctx, t) : float(getInt(_ctx, t)) / 255.f; - attribs[ET_COL].second.X = value; - attribs[ET_COL].first = true; - - const size_t propertyOffset = ET_COL_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_COL].buffer->getPointer()) + propertyOffset; + } + // null terminate the next word on the previous line and move the next word pointer along + // since we already have a full line in the buffer, we never need to retrieve more data + const char* getNextWord() + { + // move the start pointer along + StartPointer += WordLength + 1; + if (!*StartPointer) + getNextLine(); - reinterpret_cast(data)[0] = value; - } - else if (Element.Properties[i].Name == "green") + if (StartPointer==LineEndPointer) { - float value = Element.Properties[i].isFloat() ? getFloat(_ctx, t) : float(getInt(_ctx, t)) / 255.f; - attribs[ET_COL].second.Y = value; - attribs[ET_COL].first = true; - - const size_t propertyOffset = ET_COL_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_COL].buffer->getPointer()) + propertyOffset; - - reinterpret_cast(data)[1] = value; + WordLength = -1; // + return LineEndPointer; } - else if (Element.Properties[i].Name == "blue") + // process the next word { - float value = Element.Properties[i].isFloat() ? getFloat(_ctx, t) : float(getInt(_ctx, t)) / 255.f; - attribs[ET_COL].second.Z = value; - attribs[ET_COL].first = true; - - const size_t propertyOffset = ET_COL_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_COL].buffer->getPointer()) + propertyOffset; - - reinterpret_cast(data)[2] = value; + assert(LineEndPointer<=EndPointer); + const std::array WhiteSpace = {'\0',' ','\t'}; + auto wordEnd = std::find_first_of(StartPointer,LineEndPointer,WhiteSpace.begin(),WhiteSpace.end()); + // null terminate the next word + if (wordEnd!=LineEndPointer) + *(wordEnd++) = '\0'; + // find next word + auto notWhiteSpace = [WhiteSpace](const char c)->bool + { + return std::find(WhiteSpace.begin(),WhiteSpace.end(),c)==WhiteSpace.end(); + }; + auto nextWord = std::find_if(wordEnd,LineEndPointer,notWhiteSpace); + WordLength = std::distance(StartPointer,nextWord)-1; } - else if (Element.Properties[i].Name == "alpha") - { - float value = Element.Properties[i].isFloat() ? getFloat(_ctx, t) : float(getInt(_ctx, t)) / 255.f; - attribs[ET_COL].second.W = value; - attribs[ET_COL].first = true; - - const size_t propertyOffset = ET_COL_BYTESIZE * currentVertexIndex; - uint8_t* data = reinterpret_cast(outAttributes[ET_COL].buffer->getPointer()) + propertyOffset; + // return pointer to the start of current word + return StartPointer; + } + // skips x bytes in the file, getting more data if required + void moveForward(const size_t bytes) + { + assert(IsBinaryFile); + if (StartPointer+bytes>=EndPointer) + fillBuffer(); - reinterpret_cast(data)[3] = value; - } + if (StartPointer+bytes& _outIndices) -{ - if (!_ctx.IsBinaryFile) - getNextLine(_ctx); - - for (uint32_t i = 0; i < Element.Properties.size(); ++i) + // read the next int from the file and move the start pointer along + using widest_int_t = uint32_t; + widest_int_t getInt(const E_FORMAT f) { - if ((Element.Properties[i].Name == "vertex_indices" || - Element.Properties[i].Name == "vertex_index") && Element.Properties[i].Type == EPLYPT_LIST) + assert(!isFloatingPointFormat(f)); + if (IsBinaryFile) { - int32_t count = getInt(_ctx, Element.Properties[i].Data.List.CountType); - //_NBL_DEBUG_BREAK_IF(count != 3) - - uint32_t a = getInt(_ctx, Element.Properties[i].Data.List.ItemType), - b = getInt(_ctx, Element.Properties[i].Data.List.ItemType), - c = getInt(_ctx, Element.Properties[i].Data.List.ItemType); - int32_t j = 3; - - _outIndices.push_back(a); - _outIndices.push_back(b); - _outIndices.push_back(c); + if (StartPointer+sizeof(widest_int_t)>EndPointer) + fillBuffer(); - for (; j < count; ++j) + switch (getTexelOrBlockBytesize(f)) { - b = c; - c = getInt(_ctx, Element.Properties[i].Data.List.ItemType); - _outIndices.push_back(a); - _outIndices.push_back(c); - _outIndices.push_back(b); + case 1: + if (StartPointer+sizeof(int8_t)>EndPointer) + break; + return *(StartPointer++); + case 2: + { + if (StartPointer+sizeof(int16_t)>EndPointer) + break; + auto retval = *(reinterpret_cast(StartPointer)++); + if (IsWrongEndian) + retval = byteswap(retval); + return retval; + } + case 4: + { + if (StartPointer+sizeof(int32_t)>EndPointer) + break; + auto retval = *(reinterpret_cast(StartPointer)++); + if (IsWrongEndian) + retval = byteswap(retval); + return retval; + } + default: + assert(false); + break; } + return 0; } - else if (Element.Properties[i].Name == "intensity") - { - // todo: face intensity - skipProperty(_ctx, Element.Properties[i]); - } - else - skipProperty(_ctx, Element.Properties[i]); + return std::atoi(getNextWord()); } - return true; -} - - -// skips an element and all properties. return false on EOF -void CPLYMeshFileLoader::skipElement(SContext& _ctx, const SPLYElement& Element) -{ - if (_ctx.IsBinaryFile) - if (Element.IsFixedWidth) - moveForward(_ctx, Element.KnownSize); - else - for (uint32_t i = 0; i < Element.Properties.size(); ++i) - skipProperty(_ctx, Element.Properties[i]); - else - getNextLine(_ctx); -} - - -void CPLYMeshFileLoader::skipProperty(SContext& _ctx, const SPLYProperty &Property) -{ - if (Property.Type == EPLYPT_LIST) + // read the next float from the file and move the start pointer along + hlsl::float64_t getFloat(const E_FORMAT f) { - int32_t count = getInt(_ctx, Property.Data.List.CountType); + assert(isFloatingPointFormat(f)); + if (IsBinaryFile) + { + if (StartPointer+sizeof(hlsl::float64_t)>EndPointer) + fillBuffer(); - for (int32_t i=0; i < count; ++i) - getInt(_ctx, Property.Data.List.CountType); + switch (getTexelOrBlockBytesize(f)) + { + case 4: + { + if (StartPointer+sizeof(hlsl::float32_t)>EndPointer) + break; + auto retval = *(reinterpret_cast(StartPointer)++); + if (IsWrongEndian) + retval = byteswap(retval); + return retval; + } + case 8: + { + if (StartPointer+sizeof(hlsl::float64_t)>EndPointer) + break; + auto retval = *(reinterpret_cast(StartPointer)++); + if (IsWrongEndian) + retval = byteswap(retval); + return retval; + } + default: + assert(false); + break; + } + return 0; + } + return std::atoi(getNextWord()); } - else + // read the next thing from the file and move the start pointer along + void getData(void* dst, const E_FORMAT f) { - if (_ctx.IsBinaryFile) - moveForward(_ctx, Property.size()); + const auto size = getTexelOrBlockBytesize(f); + if (StartPointer+size>EndPointer) + { + fillBuffer(); + if (StartPointer+size>EndPointer) + return; + } + if (IsWrongEndian) + std::reverse_copy(StartPointer,StartPointer+size,reinterpret_cast(dst)); else - getNextWord(_ctx); - } -} - - -bool CPLYMeshFileLoader::allocateBuffer(SContext& _ctx) -{ - // Destroy the element list if it exists - _ctx.ElementList.clear(); - - if (!_ctx.Buffer) - _ctx.Buffer = _NBL_NEW_ARRAY(char, PLY_INPUT_BUFFER_SIZE); - - // not enough memory? - if (!_ctx.Buffer) - return false; - - // blank memory - memset(_ctx.Buffer, 0, PLY_INPUT_BUFFER_SIZE); - - _ctx.StartPointer = _ctx.Buffer; - _ctx.EndPointer = _ctx.Buffer; - _ctx.LineEndPointer = _ctx.Buffer - 1; - _ctx.WordLength = -1; - _ctx.EndOfFile = false; - - // get data from the file - fillBuffer(_ctx); - - return true; -} - - -// gets more data from the file. returns false on EOF -void CPLYMeshFileLoader::fillBuffer(SContext& _ctx) -{ - if (_ctx.EndOfFile) - return; - - uint32_t length = (uint32_t)(_ctx.EndPointer - _ctx.StartPointer); - if (length && _ctx.StartPointer != _ctx.Buffer) - { - // copy the remaining data to the start of the buffer - memcpy(_ctx.Buffer, _ctx.StartPointer, length); + memcpy(dst,StartPointer,size); + StartPointer += size; } - // reset start position - _ctx.StartPointer = _ctx.Buffer; - _ctx.EndPointer = _ctx.StartPointer + length; - - if (_ctx.fileOffset >= _ctx.inner.mainFile->getSize() - 1) // TODO: check it + struct SVertAttrIt { - _ctx.EndOfFile = true; - } - else + uint8_t* ptr; + uint32_t stride; + E_FORMAT dstFmt; + }; + inline void readVertex(const IAssetLoader::SAssetLoadParams& _params, const SElement& el) { - // read data from the file - system::IFile::success_t success; - _ctx.inner.mainFile->read(success, _ctx.EndPointer, _ctx.fileOffset, PLY_INPUT_BUFFER_SIZE - length); - const size_t bytesRead = success.getBytesProcessed(); - - _ctx.fileOffset += bytesRead; - // increment the end pointer by the number of bytes read - _ctx.EndPointer += bytesRead; + assert(el.Name=="vertex"); + assert(el.Properties.size()==vertAttrIts.size()); + if (!IsBinaryFile) + getNextLine(); - // if we didn't completely fill the buffer - if (bytesRead != PLY_INPUT_BUFFER_SIZE - length) + for (size_t j=0; j= _ctx.EndPointer) - fillBuffer(_ctx); - if (_ctx.StartPointer + bytes < _ctx.EndPointer) - _ctx.StartPointer += bytes; - else - _ctx.StartPointer = _ctx.EndPointer; -} - -bool CPLYMeshFileLoader::genVertBuffersForMBuffer( - asset::ICPUMeshBuffer* _mbuf, - const asset::SBufferBinding attributes[4], - SContext& context -) const -{ - core::vector availableAttributes; - for (auto i = 0; i < 4; ++i) - if (attributes[i].buffer) - availableAttributes.push_back(i); - - { - size_t check = attributes[0].buffer->getSize(); - for (size_t i = 1u; i < 4u; ++i) - { - if (attributes[i].buffer && attributes[i].buffer->getSize() != check) - return false; - else if (attributes[i].buffer) - check = attributes[i].buffer->getSize(); + const auto& prop = el.Properties[i]; + auto& it = vertAttrIts[i]; + if (!it.ptr) + { + prop.skip(*this); + continue; + } + // conversion required? + if (it.dstFmt!=prop.type) + { + assert(isIntegerFormat(it.dstFmt)==isIntegerFormat(prop.type)); + if (isIntegerFormat(it.dstFmt)) + { + uint64_t tmp = getInt(prop.type); + encodePixels(it.dstFmt,it.ptr,&tmp); + } + else + { + hlsl::float64_t tmp = getFloat(prop.type); + encodePixels(it.dstFmt,it.ptr,&tmp); + } + } + else + getData(it.ptr,prop.type); + // + it.ptr += it.stride; } } - - auto getPipeline = [&]() -> core::smart_refctd_ptr + bool readFace(const SElement& Element, core::vector& _outIndices) { - constexpr std::array avaiableOptionsForShaders { ET_COL, ET_UV, ET_NORM }; + if (!IsBinaryFile) + getNextLine(); - auto fetchPipelineFromCache = [&](CPLYMeshFileLoader::E_TYPE attribute) + for (const auto& prop : Element.Properties) { - const IAssetLoader::SAssetLoadContext fakeContext(IAssetLoader::SAssetLoadParams{}, nullptr); - const std::string hash = getPipelineCacheKey(attribute, _mbuf->getIndexBufferBinding().buffer.get()); - - const asset::IAsset::E_TYPE types[]{ asset::IAsset::ET_RENDERPASS_INDEPENDENT_PIPELINE, (asset::IAsset::E_TYPE)0u }; - auto pipelineBundle = context.loaderOverride->findCachedAsset(hash, types, fakeContext, context.topHierarchyLevel + ICPURenderpassIndependentPipeline::DESC_SET_HIERARCHYLEVELS_BELOW); + if (prop.isList() && (prop.Name=="vertex_indices" || prop.Name == "vertex_index")) { - bool status = !pipelineBundle.getContents().empty(); - assert(status); + const uint32_t count = getInt(prop.list.countType); + //_NBL_DEBUG_BREAK_IF(count != 3) + const auto srcIndexFmt = prop.list.itemType; + + _outIndices.push_back(getInt(srcIndexFmt)); + _outIndices.push_back(getInt(srcIndexFmt)); + _outIndices.push_back(getInt(srcIndexFmt)); + // TODO: handle varying vertex count faces via variable vertex count geometry collections (PLY loader should be a Geometry Collection loader) + for (auto j=3u; j(pipelineBundle.getContents().begin()[0]); - - return mbPipeline; - }; - - core::smart_refctd_ptr mbPipeline; - { - for (auto& anOption : avaiableOptionsForShaders) + else if (prop.Name == "intensity") { - auto found = std::find(availableAttributes.begin(), availableAttributes.end(), anOption); - if (found != availableAttributes.end()) - mbPipeline = fetchPipelineFromCache(static_cast(anOption)); + // todo: face intensity + prop.skip(*this); } - - if(!mbPipeline) - mbPipeline = fetchPipelineFromCache(ET_POS); + else + prop.skip(*this); } - - return mbPipeline; - }; - - auto mbPipeline = getPipeline(); - - for (auto index = 0; index < 4; ++index) - { - auto attribute = attributes[index]; - if (attribute.buffer) - _mbuf->setVertexBufferBinding(std::move(attribute), index); + return true; } - - _mbuf->setPipeline(std::move(mbPipeline)); - return true; -} + IAssetLoader::SAssetLoadContext inner; + uint32_t topHierarchyLevel; + IAssetLoader::IAssetLoaderOverride* loaderOverride; + // input buffer must be at least twice as long as the longest line in the file + std::array Buffer; // 50kb seems sane to store a line + core::vector ElementList = {}; + char* StartPointer = nullptr, *EndPointer = nullptr, *LineEndPointer = nullptr; + int32_t LineLength = 0; + int32_t WordLength = -1; // this variable is a misnomer, its really the offset to next word minus one + bool IsBinaryFile = false, IsWrongEndian = false, EndOfFile = false; + size_t fileOffset = {}; + // + core::vector vertAttrIts; +}; -E_PLY_PROPERTY_TYPE CPLYMeshFileLoader::getPropertyType(const char* typeString) const +//! creates/loads an animated mesh from the file. +SAssetBundle CPLYMeshFileLoader::loadAsset(system::IFile* _file, const IAssetLoader::SAssetLoadParams& _params, IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel) { - if (strcmp(typeString, "char") == 0 || - strcmp(typeString, "uchar") == 0 || - strcmp(typeString, "int8") == 0 || - strcmp(typeString, "uint8") == 0) - { - return EPLYPT_INT8; - } - else if (strcmp(typeString, "uint") == 0 || - strcmp(typeString, "int16") == 0 || - strcmp(typeString, "uint16") == 0 || - strcmp(typeString, "short") == 0 || - strcmp(typeString, "ushort") == 0) - { - return EPLYPT_INT16; - } - else if (strcmp(typeString, "int") == 0 || - strcmp(typeString, "long") == 0 || - strcmp(typeString, "ulong") == 0 || - strcmp(typeString, "int32") == 0 || - strcmp(typeString, "uint32") == 0) - { - return EPLYPT_INT32; - } - else if (strcmp(typeString, "float") == 0 || - strcmp(typeString, "float32") == 0) - { - return EPLYPT_FLOAT32; - } - else if (strcmp(typeString, "float64") == 0 || - strcmp(typeString, "double") == 0) - { - return EPLYPT_FLOAT64; - } - else if (strcmp(typeString, "list") == 0) - { - return EPLYPT_LIST; - } - else - { - // unsupported type. - // cannot be loaded in binary mode - return EPLYPT_UNKNOWN; - } -} + using namespace nbl::core; + if (!_file) + return {}; + SContext ctx = { + asset::IAssetLoader::SAssetLoadContext{ + _params, + _file + }, + _hierarchyLevel, + _override + }; + ctx.init(); -// Split the string data into a line in place by terminating it instead of copying. -char* CPLYMeshFileLoader::getNextLine(SContext& _ctx) -{ - // move the start pointer along - _ctx.StartPointer = _ctx.LineEndPointer + 1; + // start with empty mesh + auto geometry = make_smart_refctd_ptr(); + uint32_t vertCount=0; - // crlf split across buffer move - if (*_ctx.StartPointer == '\n') + // Currently only supports ASCII or binary meshes + if (strcmp(ctx.getNextLine(),"ply")) { - *_ctx.StartPointer = '\0'; - ++_ctx.StartPointer; + _params.logger.log("Not a valid PLY file %s", system::ILogger::ELL_ERROR,ctx.inner.mainFile->getFileName().string().c_str()); + return {}; } - // begin at the start of the next line - char* pos = _ctx.StartPointer; - while (pos < _ctx.EndPointer && *pos && *pos != '\r' && *pos != '\n') - ++pos; + // cut the next line out + ctx.getNextLine(); + // grab the word from this line + const char* word = ctx.getNextWord(); + // ignore comments + for (; strcmp(word,"comment")==0; ctx.getNextLine()) + word = ctx.getNextWord(); - if (pos < _ctx.EndPointer && (*(pos + 1) == '\r' || *(pos + 1) == '\n')) - { - *pos = '\0'; - ++pos; - } + bool readingHeader = true; + bool continueReading = true; + ctx.IsBinaryFile = false; + ctx.IsWrongEndian= false; - // we have reached the end of the buffer - if (pos >= _ctx.EndPointer) + do { - // get data from the file - if (!_ctx.EndOfFile) + if (strcmp(word,"property") == 0) { - fillBuffer(_ctx); - // reset line end pointer - _ctx.LineEndPointer = _ctx.StartPointer - 1; + word = ctx.getNextWord(); - if (_ctx.StartPointer != _ctx.EndPointer) - return getNextLine(_ctx); + if (ctx.ElementList.empty()) + { + _params.logger.log("PLY property token found before element %s", system::ILogger::ELL_WARNING, word); + } else - return _ctx.Buffer; - } - else - { - // EOF - _ctx.StartPointer = _ctx.EndPointer - 1; - *_ctx.StartPointer = '\0'; - return _ctx.StartPointer; - } - } - else - { - // null terminate the string in place - *pos = '\0'; - _ctx.LineEndPointer = pos; - _ctx.WordLength = -1; - // return pointer to the start of the line - return _ctx.StartPointer; - } -} - - -// null terminate the next word on the previous line and move the next word pointer along -// since we already have a full line in the buffer, we never need to retrieve more data -char* CPLYMeshFileLoader::getNextWord(SContext& _ctx) -{ - // move the start pointer along - _ctx.StartPointer += _ctx.WordLength + 1; - if (!*_ctx.StartPointer) - getNextLine(_ctx); - - if (_ctx.StartPointer == _ctx.LineEndPointer) - { - _ctx.WordLength = -1; // - return _ctx.LineEndPointer; - } - // begin at the start of the next word - char* pos = _ctx.StartPointer; - while (*pos && pos < _ctx.LineEndPointer && pos < _ctx.EndPointer && *pos != ' ' && *pos != '\t') - ++pos; + { + // get element + auto& el = ctx.ElementList.back(); + + // fill property struct + auto& prop = el.Properties.emplace_back(); + prop.type = prop.getType(word); + if (prop.type==EF_UNKNOWN) + { + el.KnownSize = false; - while (*pos && pos < _ctx.LineEndPointer && pos < _ctx.EndPointer && (*pos == ' ' || *pos == '\t')) - { - // null terminate the string in place - *pos = '\0'; - ++pos; - } - --pos; - _ctx.WordLength = (int32_t)(pos-_ctx.StartPointer); - // return pointer to the start of the word - return _ctx.StartPointer; -} + word = ctx.getNextWord(); + prop.list.countType = prop.getType(word); + if (ctx.IsBinaryFile && !isIntegerFormat(prop.list.countType)) + { + _params.logger.log("Cannot read binary PLY file containing data types of unknown or non integer length %s", system::ILogger::ELL_WARNING, word); + continueReading = false; + } + else + { + word = ctx.getNextWord(); + prop.list.itemType = prop.getType(word); + if (ctx.IsBinaryFile && !isIntegerFormat(prop.list.itemType)) + { + _params.logger.log("Cannot read binary PLY file containing data types of unknown or non integer length %s", system::ILogger::ELL_ERROR, word); + continueReading = false; + } + } + } + else if (ctx.IsBinaryFile && prop.type==EF_UNKNOWN) + { + _params.logger.log("Cannot read binary PLY file containing data types of unknown length %s", system::ILogger::ELL_ERROR, word); + continueReading = false; + } + else + el.KnownSize += getTexelOrBlockBytesize(prop.type); -// read the next float from the file and move the start pointer along -float CPLYMeshFileLoader::getFloat(SContext& _ctx, E_PLY_PROPERTY_TYPE t) -{ - float retVal = 0.0f; + prop.Name = ctx.getNextWord(); + } + } + else if (strcmp(word,"element")==0) + { + auto& el = ctx.ElementList.emplace_back(); + el.Name = ctx.getNextWord(); + el.Count = atoi(ctx.getNextWord()); + el.KnownSize = 0; + if (el.Name=="vertex") + vertCount = el.Count; + } + else if (strcmp(word,"comment")==0) + { + // ignore line + } + // must be `format {binary_little_endian|binary_big_endian|ascii} 1.0` + else if (strcmp(word,"format") == 0) + { + word = ctx.getNextWord(); - if (_ctx.IsBinaryFile) - { - if (_ctx.EndPointer - _ctx.StartPointer < 8) - fillBuffer(_ctx); + if (strcmp(word, "binary_little_endian") == 0) + { + ctx.IsBinaryFile = true; + } + else if (strcmp(word, "binary_big_endian") == 0) + { + ctx.IsBinaryFile = true; + ctx.IsWrongEndian = true; + } + else if (strcmp(word, "ascii")==0) + { + } + else + { + // abort if this isn't an ascii or a binary mesh + _params.logger.log("Unsupported PLY mesh format %s", system::ILogger::ELL_ERROR, word); + continueReading = false; + } - if (_ctx.EndPointer - _ctx.StartPointer > 0) - { - switch (t) + if (continueReading) { - case EPLYPT_INT8: - retVal = *_ctx.StartPointer; - _ctx.StartPointer++; - break; - case EPLYPT_INT16: - if (_ctx.IsWrongEndian) - retVal = core::Byteswap::byteswap(*(reinterpret_cast(_ctx.StartPointer))); - else - retVal = *(reinterpret_cast(_ctx.StartPointer)); - _ctx.StartPointer += 2; - break; - case EPLYPT_INT32: - if (_ctx.IsWrongEndian) - retVal = float(core::Byteswap::byteswap(*(reinterpret_cast(_ctx.StartPointer)))); - else - retVal = float(*(reinterpret_cast(_ctx.StartPointer))); - _ctx.StartPointer += 4; - break; - case EPLYPT_FLOAT32: - if (_ctx.IsWrongEndian) - retVal = core::Byteswap::byteswap(*(reinterpret_cast(_ctx.StartPointer))); - else - retVal = *(reinterpret_cast(_ctx.StartPointer)); - _ctx.StartPointer += 4; - break; - case EPLYPT_FLOAT64: - char tmp[8]; - memcpy(tmp, _ctx.StartPointer, 8); - if (_ctx.IsWrongEndian) - for (size_t i = 0u; i < 4u; ++i) - std::swap(tmp[i], tmp[7u - i]); - retVal = float(*(reinterpret_cast(tmp))); - _ctx.StartPointer += 8; - break; - case EPLYPT_LIST: - case EPLYPT_UNKNOWN: - default: - retVal = 0.0f; - _ctx.StartPointer++; // ouch! + word = ctx.getNextWord(); + if (strcmp(word, "1.0")) + { + _params.logger.log("Unsupported PLY mesh version %s",system::ILogger::ELL_WARNING,word); + } } } + else if (strcmp(word,"end_header")==0) + { + readingHeader = false; + if (ctx.IsBinaryFile) + ctx.StartPointer = ctx.LineEndPointer+1; + } else - retVal = 0.0f; - } - else - { - char* word = getNextWord(_ctx); - switch (t) { - case EPLYPT_INT8: - case EPLYPT_INT16: - case EPLYPT_INT32: - retVal = float(atoi(word)); - break; - case EPLYPT_FLOAT32: - case EPLYPT_FLOAT64: - retVal = float(atof(word)); - break; - case EPLYPT_LIST: - case EPLYPT_UNKNOWN: - default: - retVal = 0.0f; + _params.logger.log("Unknown item in PLY file %s", system::ILogger::ELL_WARNING, word); } - } - - return retVal; -} + if (readingHeader && continueReading) + { + ctx.getNextLine(); + word = ctx.getNextWord(); + } + } + while (readingHeader && continueReading); -// read the next int from the file and move the start pointer along -uint32_t CPLYMeshFileLoader::getInt(SContext& _ctx, E_PLY_PROPERTY_TYPE t) -{ - uint32_t retVal = 0; + // + if (!continueReading) + return {}; - if (_ctx.IsBinaryFile) + // now to read the actual data from the file + using index_t = uint32_t; + core::vector indices = {}; + // + auto createView = [](const E_FORMAT format, const size_t elCount)->ICPUPolygonGeometry::SDataView { - if (!_ctx.EndOfFile && _ctx.EndPointer - _ctx.StartPointer < 8) - fillBuffer(_ctx); + const auto stride = asset::getTexelOrBlockBytesize(format); + auto buffer = ICPUBuffer::create({stride*elCount}); + return { + .composed = { + .stride = stride, + .format = format, + .rangeFormat = IGeometryBase::getMatchingAABBFormat(format) + }, + .src = { + .offset = 0, + .size = buffer->getSize(), + .buffer = std::move(buffer) + } + }; + }; - if (_ctx.EndPointer - _ctx.StartPointer) + // loop through each of the elements + bool verticesProcessed = false; + for (uint32_t i=0; i https://paulbourke.net/dataformats/ply/ { - switch (t) + if (verticesProcessed) { - case EPLYPT_INT8: - retVal = *_ctx.StartPointer; - _ctx.StartPointer++; - break; - case EPLYPT_INT16: - if (_ctx.IsWrongEndian) - retVal = core::Byteswap::byteswap(*(reinterpret_cast(_ctx.StartPointer))); - else - retVal = *(reinterpret_cast(_ctx.StartPointer)); - _ctx.StartPointer += 2; - break; - case EPLYPT_INT32: - if (_ctx.IsWrongEndian) - retVal = core::Byteswap::byteswap(*(reinterpret_cast(_ctx.StartPointer))); - else - retVal = *(reinterpret_cast(_ctx.StartPointer)); - _ctx.StartPointer += 4; - break; - case EPLYPT_FLOAT32: - if (_ctx.IsWrongEndian) - retVal = (uint32_t)core::Byteswap::byteswap(*(reinterpret_cast(_ctx.StartPointer))); + _params.logger.log("Multiple `vertex` elements not supported!", system::ILogger::ELL_ERROR); + return {}; + } + ICPUPolygonGeometry::SDataViewBase posView = {}, normalView = {}; + for (auto& vertexProperty : el.Properties) + { + const auto& propertyName = vertexProperty.Name; + // only positions and normals need to be structured/canonicalized in any way + auto negotiateFormat = [&vertexProperty](ICPUPolygonGeometry::SDataViewBase& view, const uint8_t component)->void + { + assert(getFormatChannelCount(vertexProperty.type)!=0); + if (getTexelOrBlockBytesize(vertexProperty.type)>getTexelOrBlockBytesize(view.format)) + view.format = vertexProperty.type; + view.stride = hlsl::max(view.stride,component); + }; + if (propertyName=="x") + negotiateFormat(posView,0); + else if (propertyName=="y") + negotiateFormat(posView,1); + else if (propertyName=="z") + negotiateFormat(posView,2); + else if (propertyName=="nx") + negotiateFormat(normalView,0); + else if (propertyName=="ny") + negotiateFormat(normalView,1); + else if (propertyName=="nz") + negotiateFormat(normalView,2); else - retVal = (uint32_t)(*(reinterpret_cast(_ctx.StartPointer))); - _ctx.StartPointer += 4; - break; - case EPLYPT_FLOAT64: - // todo: byteswap 64-bit - retVal = (uint32_t)(*(reinterpret_cast(_ctx.StartPointer))); - _ctx.StartPointer += 8; - break; - case EPLYPT_LIST: - case EPLYPT_UNKNOWN: - default: - retVal = 0; - _ctx.StartPointer++; // ouch! + { +// TODO: record the `propertyName` + geometry->getAuxAttributeViews()->push_back(createView(vertexProperty.type,el.Count)); + } } + auto setFinalFormat = [&ctx](ICPUPolygonGeometry::SDataViewBase& view)->void + { + const auto componentFormat = view.format; + const auto componentCount = view.stride+1; + // turn single channel format to multiple + view.format = [=]()->E_FORMAT + { + switch (view.format) + { + case EF_R8_SINT: + switch (componentCount) + { + case 1: + return EF_R8_SINT; + case 2: + return EF_R8G8_SINT; + case 3: + return EF_R8G8B8_SINT; + case 4: + return EF_R8G8B8A8_SINT; + default: + break; + } + break; + case EF_R8_UINT: + switch (componentCount) + { + case 1: + return EF_R8_UINT; + case 2: + return EF_R8G8_UINT; + case 3: + return EF_R8G8B8_UINT; + case 4: + return EF_R8G8B8A8_UINT; + default: + break; + } + break; + case EF_R16_SINT: + switch (componentCount) + { + case 1: + return EF_R16_SINT; + case 2: + return EF_R16G16_SINT; + case 3: + return EF_R16G16B16_SINT; + case 4: + return EF_R16G16B16A16_SINT; + default: + break; + } + break; + case EF_R16_UINT: + switch (componentCount) + { + case 1: + return EF_R16_UINT; + case 2: + return EF_R16G16_UINT; + case 3: + return EF_R16G16B16_UINT; + case 4: + return EF_R16G16B16A16_UINT; + default: + break; + } + break; + case EF_R32_SINT: + switch (componentCount) + { + case 1: + return EF_R32_SINT; + case 2: + return EF_R32G32_SINT; + case 3: + return EF_R32G32B32_SINT; + case 4: + return EF_R32G32B32A32_SINT; + default: + break; + } + break; + case EF_R32_UINT: + switch (componentCount) + { + case 1: + return EF_R32_UINT; + case 2: + return EF_R32G32_UINT; + case 3: + return EF_R32G32B32_UINT; + case 4: + return EF_R32G32B32A32_UINT; + default: + break; + } + break; + case EF_R32_SFLOAT: + switch (componentCount) + { + case 1: + return EF_R32_SFLOAT; + case 2: + return EF_R32G32_SFLOAT; + case 3: + return EF_R32G32B32_SFLOAT; + case 4: + return EF_R32G32B32A32_SFLOAT; + default: + break; + } + break; + case EF_R64_SFLOAT: + switch (componentCount) + { + case 1: + return EF_R64_SFLOAT; + case 2: + return EF_R64G64_SFLOAT; + case 3: + return EF_R64G64B64_SFLOAT; + case 4: + return EF_R64G64B64A64_SFLOAT; + default: + break; + } + break; + default: + break; + } + return EF_UNKNOWN; + }(); + view.stride = getTexelOrBlockBytesize(view.format); + // + for (auto c=0u; c(offset), + .stride = view.stride, + .dstFmt = componentFormat + }); + } + }; + if (posView.format!=EF_UNKNOWN) + { + auto beginIx = ctx.vertAttrIts.size(); + setFinalFormat(posView); + auto view = createView(posView.format,el.Count); + for (const auto size=ctx.vertAttrIts.size(); beginIx!=size; beginIx++) + ctx.vertAttrIts[beginIx].ptr += ptrdiff_t(view.src.buffer->getPointer())+view.src.offset; + geometry->setPositionView(std::move(view)); + } + if (normalView.format!=EF_UNKNOWN) + { + auto beginIx = ctx.vertAttrIts.size(); + setFinalFormat(normalView); + auto view = createView(normalView.format,el.Count); + for (const auto size=ctx.vertAttrIts.size(); beginIx!=size; beginIx++) + ctx.vertAttrIts[beginIx].ptr += ptrdiff_t(view.src.buffer->getPointer())+view.src.offset; + geometry->setNormalView(std::move(view)); + } + // + for (auto& view : *geometry->getAuxAttributeViews()) + ctx.vertAttrIts.push_back({ + .ptr = reinterpret_cast(view.src.buffer->getPointer())+view.src.offset, + .stride = getTexelOrBlockBytesize(view.composed.format), + .dstFmt = view.composed.format + }); + // loop through vertex properties + ctx.readVertex(_params,el); + verticesProcessed = true; + } + else if (el.Name=="face") + { + for (size_t j=0; jsetIndexing(IPolygonGeometryBase::PointList()); } else { - char* word = getNextWord(_ctx); - switch (t) - { - case EPLYPT_INT8: - case EPLYPT_INT16: - case EPLYPT_INT32: - retVal = atoi(word); - break; - case EPLYPT_FLOAT32: - case EPLYPT_FLOAT64: - retVal = uint32_t(atof(word)); - break; - case EPLYPT_LIST: - case EPLYPT_UNKNOWN: - default: - retVal = 0; - } + geometry->setIndexing(IPolygonGeometryBase::TriangleList()); + auto buffer = ICPUBuffer::create({{indices.size()*sizeof(index_t),IBuffer::EUF_INDEX_BUFFER_BIT},indices.data()}); + hlsl::shapes::AABB<4,index_t> aabb; + aabb.minVx[0] = *std::min_element(indices.begin(),indices.end()); + aabb.maxVx[0] = *std::max_element(indices.begin(),indices.end()); + geometry->setIndexView({ + .composed = { + .encodedDataRange = {.u32=aabb}, + .stride = sizeof(index_t), + .format = EF_R32_UINT, + .rangeFormat = IGeometryBase::EAABBFormat::U32 + }, + .src = {.offset=0,.size=buffer->getSize(),.buffer=std::move(buffer)} + }); } - return retVal; -} + CPolygonGeometryManipulator::recomputeContentHashes(geometry.get()); + CPolygonGeometryManipulator::recomputeRanges(geometry.get()); + + auto meta = core::make_smart_refctd_ptr(); + return SAssetBundle(std::move(meta),{std::move(geometry)}); +} -} // end namespace scene -} // end namespace nbl +} // end namespace nbl::asset #endif // _NBL_COMPILE_WITH_PLY_LOADER_ diff --git a/src/nbl/asset/interchange/CPLYMeshFileLoader.h b/src/nbl/asset/interchange/CPLYMeshFileLoader.h index ba699f044f..3c7775018f 100644 --- a/src/nbl/asset/interchange/CPLYMeshFileLoader.h +++ b/src/nbl/asset/interchange/CPLYMeshFileLoader.h @@ -1,218 +1,39 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors #ifndef _NBL_ASSET_C_PLY_MESH_FILE_LOADER_H_INCLUDED_ #define _NBL_ASSET_C_PLY_MESH_FILE_LOADER_H_INCLUDED_ +#ifdef _NBL_COMPILE_WITH_PLY_LOADER_ #include "nbl/core/declarations.h" -#include "nbl/asset/interchange/IAssetLoader.h" -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" + +#include "nbl/asset/interchange/IGeometryLoader.h" + +#include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/metadata/CPLYMetadata.h" namespace nbl::asset { -// input buffer must be at least twice as long as the longest line in the file -#define PLY_INPUT_BUFFER_SIZE 51200 // file is loaded in 50k chunks - -enum E_PLY_PROPERTY_TYPE -{ - EPLYPT_INT8 = 0, - EPLYPT_INT16, - EPLYPT_INT32, - EPLYPT_FLOAT32, - EPLYPT_FLOAT64, - EPLYPT_LIST, - EPLYPT_UNKNOWN -}; - //! Meshloader capable of loading obj meshes. -class CPLYMeshFileLoader : public IRenderpassIndependentPipelineLoader +class CPLYMeshFileLoader final : public IGeometryLoader { -protected: - //! Destructor - virtual ~CPLYMeshFileLoader(); - -public: - //! Constructor - CPLYMeshFileLoader(IAssetManager* _am); - - virtual bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const override; - - virtual const char** getAssociatedFileExtensions() const override - { - static const char* ext[]{ "ply", nullptr }; - return ext; - } + public: + inline CPLYMeshFileLoader() = default; - virtual uint64_t getSupportedAssetTypesBitfield() const override { return IAsset::ET_MESH; } + bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const override; - //! creates/loads an animated mesh from the file. - virtual SAssetBundle loadAsset(system::IFile* _file, const IAssetLoader::SAssetLoadParams& _params, IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; - -private: - - virtual void initialize() override; - - enum E_TYPE { ET_POS = 0, ET_UV = 2, ET_NORM = 3, ET_COL = 1 }; - - static const std::string getPipelineCacheKey(E_TYPE type, bool indexBufferBindingAvailable) - { - auto getTypeHash = [&]() -> std::string + const char** getAssociatedFileExtensions() const override { - bool status = true; - - switch (type) - { - case ET_POS: - return "nbl/builtin/pipeline/loader/PLY/only_position_attribute/"; - case ET_COL: - return "nbl/builtin/pipeline/loader/PLY/color_attribute/"; - case ET_UV: - return "nbl/builtin/pipeline/loader/PLY/uv_attribute/"; - case ET_NORM: - return "nbl/builtin/pipeline/loader/PLY/normal_attribute/"; - default: - { - status = false; - assert(status); - } - } - return (const char*)nullptr; - }; - - return getTypeHash() + (indexBufferBindingAvailable ? "triangle_list" : "point_list"); - } - - struct SPLYProperty - { - std::string Name; - E_PLY_PROPERTY_TYPE Type; - #include "nbl/nblpack.h" - union - { - uint8_t Int8; - uint16_t Int16; - uint32_t Int32; - float Float32; - double Double; - struct SPLYListProperty - { - E_PLY_PROPERTY_TYPE CountType; - E_PLY_PROPERTY_TYPE ItemType; - } List PACK_STRUCT; - - } Data PACK_STRUCT; - #include "nbl/nblunpack.h" - - inline uint32_t size() const - { - switch(Type) - { - case EPLYPT_INT8: - return 1; - case EPLYPT_INT16: - return 2; - case EPLYPT_INT32: - case EPLYPT_FLOAT32: - return 4; - case EPLYPT_FLOAT64: - return 8; - case EPLYPT_LIST: - case EPLYPT_UNKNOWN: - default: - return 0; - } - } - - inline bool isFloat() const - { - switch(Type) - { - case EPLYPT_FLOAT32: - case EPLYPT_FLOAT64: - return true; - case EPLYPT_INT8: - case EPLYPT_INT16: - case EPLYPT_INT32: - case EPLYPT_LIST: - case EPLYPT_UNKNOWN: - default: - return false; - } - } - }; - - struct SPLYElement - { - // name of the element. We only want "vertex" and "face" elements - // but we have to parse the others anyway. - std::string Name; - // The number of elements in the file - uint32_t Count; - // Properties of this element - core::vector Properties; - // in binary files, true if this is a fixed size - bool IsFixedWidth; - // known size in bytes, 0 if unknown - uint32_t KnownSize; - }; - - struct SContext - { - ~SContext() - { - if (Buffer) - { - _NBL_DELETE_ARRAY(reinterpret_cast(Buffer), PLY_INPUT_BUFFER_SIZE); - Buffer = nullptr; - } - ElementList.clear(); + static const char* ext[]{ "ply", nullptr }; + return ext; } - IAssetLoader::SAssetLoadContext inner; - uint32_t topHierarchyLevel; - IAssetLoader::IAssetLoaderOverride* loaderOverride; - - core::vector> ElementList; - - char* Buffer = nullptr; - bool IsBinaryFile = false, IsWrongEndian = false, EndOfFile = false; - int32_t LineLength = 0, WordLength = 0; - char* StartPointer = nullptr, *EndPointer = nullptr, *LineEndPointer = nullptr; - size_t fileOffset = {}; - }; - - bool allocateBuffer(SContext& _ctx); - char* getNextLine(SContext& _ctx); - char* getNextWord(SContext& _ctx); - void fillBuffer(SContext& _ctx); - E_PLY_PROPERTY_TYPE getPropertyType(const char* typeString) const; - - bool readVertex(SContext& _ctx, const SPLYElement &Element, asset::SBufferBinding outAttributes[4], const uint32_t& currentVertexIndex, const IAssetLoader::SAssetLoadParams& _params); - bool readFace(SContext& _ctx, const SPLYElement &Element, core::vector& _outIndices); - - void skipElement(SContext& _ctx, const SPLYElement &Element); - void skipProperty(SContext& _ctx, const SPLYProperty &Property); - float getFloat(SContext& _ctx, E_PLY_PROPERTY_TYPE t); - uint32_t getInt(SContext& _ctx, E_PLY_PROPERTY_TYPE t); - void moveForward(SContext& _ctx, uint32_t bytes); - - bool genVertBuffersForMBuffer( - ICPUMeshBuffer* _mbuf, - const asset::SBufferBinding attributes[4], - SContext& context - ) const; - - template - static inline void performActionBasedOnOrientationSystem(aType& varToHandle, void (*performOnCertainOrientation)(aType& varToHandle)) - { - performOnCertainOrientation(varToHandle); - } + //! creates/loads an animated mesh from the file. + virtual SAssetBundle loadAsset(system::IFile* _file, const IAssetLoader::SAssetLoadParams& _params, IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; }; } // end namespace nbl::asset - +#endif #endif diff --git a/src/nbl/asset/interchange/CPLYMeshWriter.h b/src/nbl/asset/interchange/CPLYMeshWriter.h index 1de4e14142..e709ffa0fe 100644 --- a/src/nbl/asset/interchange/CPLYMeshWriter.h +++ b/src/nbl/asset/interchange/CPLYMeshWriter.h @@ -1,26 +1,24 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors +#ifndef _NBL_ASSET_PLY_MESH_WRITER_H_INCLUDED_ +#define _NBL_ASSET_PLY_MESH_WRITER_H_INCLUDED_ -#ifndef __NBL_ASSET_PLY_MESH_WRITER_H_INCLUDED__ -#define __NBL_ASSET_PLY_MESH_WRITER_H_INCLUDED__ + +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/asset/interchange/IGeometryWriter.h" #include -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/interchange/IAssetWriter.h" -namespace nbl -{ -namespace asset +namespace nbl::asset { //! class to write PLY mesh files -class CPLYMeshWriter : public asset::IAssetWriter +class CPLYMeshWriter : public IGeometryWriter { public: - CPLYMeshWriter(); virtual const char** getAssociatedFileExtensions() const @@ -29,8 +27,6 @@ class CPLYMeshWriter : public asset::IAssetWriter return ext; } - virtual uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MESH; } - virtual uint32_t getSupportedFlags() override { return asset::EWF_BINARY; } virtual uint32_t getForcedFlags() { return 0u; } @@ -45,13 +41,13 @@ class CPLYMeshWriter : public asset::IAssetWriter size_t fileOffset = 0; }; - void writeBinary(const asset::ICPUMeshBuffer* _mbuf, size_t _vtxCount, size_t _fcCount, asset::E_INDEX_TYPE _idxType, void* const _indices, bool _forceFaces, const bool _vaidToWrite[4], SContext& context) const; - void writeText(const asset::ICPUMeshBuffer* _mbuf, size_t _vtxCount, size_t _fcCount, asset::E_INDEX_TYPE _idxType, void* const _indices, bool _forceFaces, const bool _vaidToWrite[4], SContext& context) const; + void writeBinary(const ICPUPolygonGeometry* geom, size_t _vtxCount, size_t _fcCount, asset::E_INDEX_TYPE _idxType, void* const _indices, bool _forceFaces, const bool _vaidToWrite[4], SContext& context) const; + void writeText(const ICPUPolygonGeometry* geom, size_t _vtxCount, size_t _fcCount, asset::E_INDEX_TYPE _idxType, void* const _indices, bool _forceFaces, const bool _vaidToWrite[4], SContext& context) const; - void writeAttribBinary(SContext& context, asset::ICPUMeshBuffer* _mbuf, uint32_t _vaid, size_t _ix, size_t _cpa, bool flipAttribute = false) const; + void writeAttribBinary(SContext& context, ICPUPolygonGeometry* geom, uint32_t _vaid, size_t _ix, size_t _cpa, bool flipAttribute = false) const; - //! Creates new mesh buffer with the same attribute buffers mapped but with normalized types changed to corresponding true integer types. - static core::smart_refctd_ptr createCopyMBuffNormalizedReplacedWithTrueInt(const asset::ICPUMeshBuffer* _mbuf); + //! Creates new geometry with the same attribute buffers mapped but with normalized types changed to corresponding true integer types. + static core::smart_refctd_ptr createCopyNormalizedReplacedWithTrueInt(const ICPUPolygonGeometry* geom); static std::string getTypeString(asset::E_FORMAT _t); @@ -80,6 +76,4 @@ class CPLYMeshWriter : public asset::IAssetWriter }; } // end namespace -} // end namespace - #endif diff --git a/src/nbl/asset/interchange/CSTLMeshFileLoader.h b/src/nbl/asset/interchange/CSTLMeshFileLoader.h index fc35134237..f7020ab292 100644 --- a/src/nbl/asset/interchange/CSTLMeshFileLoader.h +++ b/src/nbl/asset/interchange/CSTLMeshFileLoader.h @@ -1,24 +1,22 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors +#ifndef _NBL_ASSET_C_STL_MESH_FILE_LOADER_H_INCLUDED_ +#define _NBL_ASSET_C_STL_MESH_FILE_LOADER_H_INCLUDED_ -#ifndef __NBL_ASSET_C_STL_MESH_FILE_LOADER_H_INCLUDED__ -#define __NBL_ASSET_C_STL_MESH_FILE_LOADER_H_INCLUDED__ #include "nbl/core/declarations.h" -#include "nbl/asset/interchange/IAssetLoader.h" -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" + +#include "nbl/asset/interchange/IGeometryLoader.h" #include "nbl/asset/metadata/CSTLMetadata.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { //! Meshloader capable of loading STL meshes. -class CSTLMeshFileLoader final : public IRenderpassIndependentPipelineLoader +class CSTLMeshFileLoader final : public IGeometryLoader { public: @@ -34,10 +32,7 @@ class CSTLMeshFileLoader final : public IRenderpassIndependentPipelineLoader return ext; } - uint64_t getSupportedAssetTypesBitfield() const override { return IAsset::ET_MESH; } - private: - struct SContext { IAssetLoader::SAssetLoadContext inner; @@ -61,17 +56,9 @@ class CSTLMeshFileLoader final : public IRenderpassIndependentPipelineLoader //! Read 3d vector of floats void getNextVector(SContext* context, core::vectorSIMDf& vec, bool binary) const; - template - static inline void performActionBasedOnOrientationSystem(aType& varToHandle, void (*performOnCertainOrientation)(aType& varToHandle)) - { - performOnCertainOrientation(varToHandle); - } - asset::IAssetManager* m_assetMgr; }; -} // end namespace scene -} // end namespace nbl - +} // end namespace nbl::scene #endif diff --git a/src/nbl/asset/interchange/CSTLMeshWriter.h b/src/nbl/asset/interchange/CSTLMeshWriter.h index 3278c5c4f5..a25a84534c 100644 --- a/src/nbl/asset/interchange/CSTLMeshWriter.h +++ b/src/nbl/asset/interchange/CSTLMeshWriter.h @@ -1,21 +1,20 @@ -// Copyright (C) 2019 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2019-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine" and was originally part of the "Irrlicht Engine" // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors +#ifndef _NBL_ASSET_STL_MESH_WRITER_H_INCLUDED_ +#define _NBL_ASSET_STL_MESH_WRITER_H_INCLUDED_ -#ifndef __NBL_ASSET_STL_MESH_WRITER_H_INCLUDED__ -#define __NBL_ASSET_STL_MESH_WRITER_H_INCLUDED__ -#include "nbl/asset/ICPUMesh.h" -#include "nbl/asset/interchange/IAssetWriter.h" +#include "nbl/asset/ICPUPolygonGeometry.h" +#include "nbl/asset/interchange/IGeometryWriter.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { //! class to write meshes, implementing a STL writer -class CSTLMeshWriter : public asset::IAssetWriter +class CSTLMeshWriter : public IGeometryWriter { protected: virtual ~CSTLMeshWriter(); @@ -29,8 +28,6 @@ class CSTLMeshWriter : public asset::IAssetWriter return ext; } - virtual uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MESH; } - virtual uint32_t getSupportedFlags() override { return asset::EWF_BINARY; } virtual uint32_t getForcedFlags() { return 0u; } @@ -46,20 +43,17 @@ class CSTLMeshWriter : public asset::IAssetWriter }; // write binary format - bool writeMeshBinary(const asset::ICPUMesh* mesh, SContext* context); + bool writeMeshBinary(const ICPUPolygonGeometry* geom, SContext* context); // write text format - bool writeMeshASCII(const asset::ICPUMesh* mesh, SContext* context); + bool writeMeshASCII(const ICPUPolygonGeometry* geom, SContext* context); // create vector output with line end into string void getVectorAsStringLine(const core::vectorSIMDf& v, std::string& s) const; // write face information to file - void writeFaceText(const core::vectorSIMDf& v1, - const core::vectorSIMDf& v2, const core::vectorSIMDf& v3, SContext* context); + void writeFaceText(const core::vectorSIMDf& v1, const core::vectorSIMDf& v2, const core::vectorSIMDf& v3, SContext* context); }; } // end namespace -} // end namespace - #endif diff --git a/src/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.cpp b/src/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.cpp deleted file mode 100644 index 7c09ef88cc..0000000000 --- a/src/nbl/asset/interchange/IRenderpassIndependentPipelineLoader.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#include "nbl/asset/interchange/IRenderpassIndependentPipelineLoader.h" -#include "nbl/asset/asset_utils.h" - -using namespace nbl; -using namespace asset; - -IRenderpassIndependentPipelineLoader::~IRenderpassIndependentPipelineLoader() -{ -} - -void IRenderpassIndependentPipelineLoader::initialize() -{ - auto dfltOver = IAssetLoaderOverride(m_assetMgr); - const IAssetLoader::SAssetLoadContext fakeCtx(IAssetLoader::SAssetLoadParams{},nullptr); - - // find ds1 layout - auto ds1layout = dfltOver.findDefaultAsset("nbl/builtin/descriptor_set_layout/basic_view_parameters",fakeCtx,0u).first; - - // create common metadata part - { - constexpr size_t DS1_METADATA_ENTRY_CNT = 3ull; - m_basicViewParamsSemantics = core::make_refctd_dynamic_array(DS1_METADATA_ENTRY_CNT); - - constexpr IRenderpassIndependentPipelineMetadata::E_COMMON_SHADER_INPUT types[DS1_METADATA_ENTRY_CNT] = - { - IRenderpassIndependentPipelineMetadata::ECSI_WORLD_VIEW_PROJ, - IRenderpassIndependentPipelineMetadata::ECSI_WORLD_VIEW, - IRenderpassIndependentPipelineMetadata::ECSI_WORLD_VIEW_INVERSE_TRANSPOSE - }; - constexpr uint32_t sizes[DS1_METADATA_ENTRY_CNT] = - { - sizeof(SBasicViewParameters::MVP), - sizeof(SBasicViewParameters::MV), - sizeof(SBasicViewParameters::NormalMat) - }; - constexpr uint32_t relOffsets[DS1_METADATA_ENTRY_CNT] = - { - offsetof(SBasicViewParameters,MVP), - offsetof(SBasicViewParameters,MV), - offsetof(SBasicViewParameters,NormalMat) - }; - for (uint32_t i=0u; iend()-i-1u)[0]; - semantic.type = types[i]; - semantic.descriptorSection.type = IRenderpassIndependentPipelineMetadata::ShaderInput::E_TYPE::ET_UNIFORM_BUFFER; - semantic.descriptorSection.uniformBufferObject.binding = ds1layout->getDescriptorRedirect(IDescriptor::E_TYPE::ET_UNIFORM_BUFFER).getBinding(asset::ICPUDescriptorSetLayout::CBindingRedirect::storage_range_index_t{ 0 }).data; - semantic.descriptorSection.uniformBufferObject.set = 1u; - semantic.descriptorSection.uniformBufferObject.relByteoffset = relOffsets[i]; - semantic.descriptorSection.uniformBufferObject.bytesize = sizes[i]; - semantic.descriptorSection.shaderAccessFlags = hlsl::ShaderStage::ESS_VERTEX; - } - } -} diff --git a/src/nbl/asset/pch_asset.h b/src/nbl/asset/pch_asset.h index afd80bce51..361df786f1 100644 --- a/src/nbl/asset/pch_asset.h +++ b/src/nbl/asset/pch_asset.h @@ -1,9 +1,8 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_PCH_ASSET_H_INCLUDED__ -#define __NBL_ASSET_PCH_ASSET_H_INCLUDED__ +#ifndef _NBL_ASSET_PCH_ASSET_H_INCLUDED_ +#define _NBL_ASSET_PCH_ASSET_H_INCLUDED_ #include "nbl/asset/asset.h" @@ -33,8 +32,6 @@ #include "nbl/asset/utils/IShaderCompiler.h" -// mesh -#include "nbl/asset/utils/CGeometryCreator.h" // loaders #include "nbl/asset/interchange/COBJMeshFileLoader.h" #include "nbl/asset/interchange/CPLYMeshFileLoader.h" @@ -43,10 +40,10 @@ #include "nbl/asset/interchange/CPLYMeshWriter.h" #include "nbl/asset/interchange/CSTLMeshWriter.h" // manipulation +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" #include "nbl/asset/utils/CForsythVertexCacheOptimizer.h" #include "nbl/asset/utils/CSmoothNormalGenerator.h" -#include "nbl/asset/utils/COverdrawMeshOptimizer.h" -#include "nbl/asset/utils/CMeshManipulator.h" +#include "nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h" #endif //_NBL_PCH_IGNORE_PRIVATE_HEADERS diff --git a/src/nbl/asset/utils/CGLSLCompiler.cpp b/src/nbl/asset/utils/CGLSLCompiler.cpp index ca6ee329a8..723bad2a7b 100644 --- a/src/nbl/asset/utils/CGLSLCompiler.cpp +++ b/src/nbl/asset/utils/CGLSLCompiler.cpp @@ -61,20 +61,18 @@ namespace nbl::asset::impl shaderc_include_result* res = new shaderc_include_result; std::filesystem::path relDir; - #ifdef NBL_EMBED_BUILTIN_RESOURCES + #ifndef NBL_EMBED_BUILTIN_RESOURCES + const bool reqBuiltin = false; + #else const bool reqFromBuiltin = builtin::hasPathPrefix(_requesting_source); const bool reqBuiltin = builtin::hasPathPrefix(_requested_source); + //While #includ'ing a builtin, one must specify its full path (starting with "nbl/builtin" or "/nbl/builtin"). + // This rule applies also while a builtin is #includ`ing another builtin. + //While including a filesystem file it must be either absolute path (or relative to any search dir added to asset::iIncludeHandler; <>-type), + // or path relative to executable's working directory (""-type). if (!reqFromBuiltin && !reqBuiltin) - { - //While #includ'ing a builtin, one must specify its full path (starting with "nbl/builtin" or "/nbl/builtin"). - // This rule applies also while a builtin is #includ`ing another builtin. - //While including a filesystem file it must be either absolute path (or relative to any search dir added to asset::iIncludeHandler; <>-type), - // or path relative to executable's working directory (""-type). + #endif // NBL_EMBED_BUILTIN_RESOURCES relDir = std::filesystem::path(_requesting_source).parent_path(); - } - #else - const bool reqBuiltin = false; - #endif // NBL_EMBED_BUILTIN_RESOURCES std::filesystem::path name = (_type == shaderc_include_type_relative) ? (relDir / _requested_source) : (_requested_source); if (std::filesystem::exists(name) && !reqBuiltin) diff --git a/src/nbl/asset/utils/CGeometryCreator.cpp b/src/nbl/asset/utils/CGeometryCreator.cpp index ea18b4bf2b..c5c6ac6765 100644 --- a/src/nbl/asset/utils/CGeometryCreator.cpp +++ b/src/nbl/asset/utils/CGeometryCreator.cpp @@ -2,44 +2,33 @@ // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h + +#include "nbl/asset/utils/CGeometryCreator.h" +#include "nbl/builtin/hlsl/tgmath.hlsl" + #include #include #include +#include -#include "nbl/asset/utils/CGeometryCreator.h" -#include "nbl/asset/utils/CQuantNormalCache.h" namespace nbl::asset { -CGeometryCreator::CGeometryCreator(IMeshManipulator* const _defaultMeshManipulator) - : defaultMeshManipulator(_defaultMeshManipulator) -{ - if (defaultMeshManipulator == nullptr) - { - _NBL_DEBUG_BREAK_IF(true); - assert(false); - } -} - -CGeometryCreator::return_type CGeometryCreator::createCubeMesh(const core::vector3df& size) const +core::smart_refctd_ptr CGeometryCreator::createCube(const hlsl::float32_t3 size) const { - return_type retval; + using namespace hlsl; - constexpr size_t vertexSize = sizeof(CGeometryCreator::CubeVertex); - retval.inputParams = {0b1111u,0b1u,{ - {0u,EF_R32G32B32_SFLOAT,offsetof(CubeVertex,pos)}, - {0u,EF_R8G8B8A8_UNORM,offsetof(CubeVertex,color)}, - {0u,EF_R8G8_USCALED,offsetof(CubeVertex,uv)}, - {0u,EF_R8G8B8_SSCALED,offsetof(CubeVertex,normal)} - },{vertexSize,SVertexInputBindingParams::EVIR_PER_VERTEX}}; + auto retval = core::make_smart_refctd_ptr(); + retval->setIndexing(IPolygonGeometryBase::TriangleList()); // Create indices + using index_t = uint16_t; { - retval.indexCount = 36u; - auto indices = asset::ICPUBuffer::create({ sizeof(uint16_t)*retval.indexCount }); - indices->addUsageFlags(asset::IBuffer::EUF_INDEX_BUFFER_BIT); - auto u = reinterpret_cast(indices->getPointer()); + constexpr auto IndexCount = 36u; + constexpr auto bytesize = sizeof(index_t) * IndexCount; + auto indices = ICPUBuffer::create({bytesize,IBuffer::EUF_INDEX_BUFFER_BIT}); + auto u = reinterpret_cast(indices->getPointer()); for (uint32_t i=0u; i<6u; ++i) { u[i*6+0] = 4*i+0; @@ -49,118 +38,170 @@ CGeometryCreator::return_type CGeometryCreator::createCubeMesh(const core::vecto u[i*6+4] = 4*i+2; u[i*6+5] = 4*i+3; } - retval.indexBuffer = {0ull,std::move(indices)}; + shapes::AABB<4,index_t> aabb; + aabb.minVx[0] = 0; + aabb.maxVx[0] = 23; + retval->setIndexView({ + .composed = { + .encodedDataRange = {.u16=aabb}, + .stride = sizeof(index_t), + .format = EF_R16_UINT, + .rangeFormat = IGeometryBase::EAABBFormat::U16 + }, + .src = {.offset=0,.size=bytesize,.buffer=std::move(indices)} + }); } - // Create vertices - auto vertices = asset::ICPUBuffer::create({ 24u*vertexSize }); - vertices->addUsageFlags(IBuffer::EUF_VERTEX_BUFFER_BIT); - CubeVertex* ptr = (CubeVertex*)vertices->getPointer(); + constexpr auto CubeUniqueVertices = 24; - const core::vector3d normals[6] = - { - core::vector3d(0, 0, 1), - core::vector3d(1, 0, 0), - core::vector3d(0, 0, -1), - core::vector3d(-1, 0, 0), - core::vector3d(0, 1, 0), - core::vector3d(0, -1, 0) - }; - const core::vector3df pos[8] = - { - core::vector3df(-0.5f,-0.5f, 0.5f)*size, - core::vector3df( 0.5f,-0.5f, 0.5f)*size, - core::vector3df( 0.5f, 0.5f, 0.5f)*size, - core::vector3df(-0.5f, 0.5f, 0.5f)*size, - core::vector3df( 0.5f,-0.5f,-0.5f)*size, - core::vector3df(-0.5f, 0.5f,-0.5f)*size, - core::vector3df(-0.5f,-0.5f,-0.5f)*size, - core::vector3df( 0.5f, 0.5f,-0.5f)*size - }; - const core::vector2d uvs[4] = + // Create vertex attributes with NONE usage because we have no clue how they'll be used + hlsl::float32_t3* positions; + // for now because no reliable RGB10A2 encode and scant support for 24-bit UTB formats + hlsl::vector* normals; + hlsl::vector* uvs; { - core::vector2d(0, 1), - core::vector2d(1, 1), - core::vector2d(1, 0), - core::vector2d(0, 0) - }; + { + constexpr auto AttrSize = sizeof(decltype(*positions)); + auto buff = ICPUBuffer::create({AttrSize*CubeUniqueVertices,IBuffer::EUF_NONE}); + positions = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,float32_t> aabb; + aabb.maxVx = float32_t4(size*0.5f,0.f); + aabb.minVx = -aabb.maxVx; + retval->setPositionView({ + .composed = { + .encodedDataRange = {.f32=aabb}, + .stride = AttrSize, + .format = EF_R32G32B32_SFLOAT, + .rangeFormat = IGeometryBase::EAABBFormat::F32 + }, + .src = {.offset=0,.size=buff->getSize(),.buffer = std::move(buff)} + }); + } + { + constexpr auto AttrSize = sizeof(decltype(*normals)); + auto buff = ICPUBuffer::create({AttrSize*CubeUniqueVertices,IBuffer::EUF_NONE}); + normals = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,int8_t> aabb; + aabb.maxVx = hlsl::vector(127,127,127,0); + aabb.minVx = -aabb.maxVx; + retval->setNormalView({ + .composed = { + .encodedDataRange = {.s8=aabb}, + .stride = AttrSize, + .format = EF_R8G8B8A8_SNORM, + .rangeFormat = IGeometryBase::EAABBFormat::S8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + { + constexpr auto AttrSize = sizeof(decltype(*uvs)); + auto buff = ICPUBuffer::create({AttrSize*CubeUniqueVertices,IBuffer::EUF_NONE}); + uvs = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,uint8_t> aabb; + aabb.minVx = hlsl::vector(0,0,0,0); + aabb.maxVx = hlsl::vector(255,255,0,0); + retval->getAuxAttributeViews()->push_back({ + .composed = { + .encodedDataRange = {.u8=aabb}, + .stride = AttrSize, + .format = EF_R8G8_UNORM, + .rangeFormat = IGeometryBase::EAABBFormat::U8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + } - for (size_t f=0ull; f<6ull; ++f) + // { - const size_t v = f*4ull; - - for (size_t i=0ull; i<4ull; ++i) + const hlsl::float32_t3 pos[8] = { - const core::vector3d& n = normals[f]; - const core::vector2d& uv = uvs[i]; - ptr[v+i].setColor(255, 255, 255, 255); - ptr[v+i].setNormal(n.X, n.Y, n.Z); - ptr[v+i].setUv(uv.X, uv.Y); - } + hlsl::float32_t3(-0.5f,-0.5f, 0.5f) * size, + hlsl::float32_t3(0.5f,-0.5f, 0.5f) * size, + hlsl::float32_t3(0.5f, 0.5f, 0.5f) * size, + hlsl::float32_t3(-0.5f, 0.5f, 0.5f) * size, + hlsl::float32_t3(0.5f,-0.5f,-0.5f) * size, + hlsl::float32_t3(-0.5f, 0.5f,-0.5f) * size, + hlsl::float32_t3(-0.5f,-0.5f,-0.5f) * size, + hlsl::float32_t3(0.5f, 0.5f,-0.5f) * size + }; + positions[0] = hlsl::float32_t3(pos[0][0], pos[0][1], pos[0][2]); + positions[1] = hlsl::float32_t3(pos[1][0], pos[1][1], pos[1][2]); + positions[2] = hlsl::float32_t3(pos[2][0], pos[2][1], pos[2][2]); + positions[3] = hlsl::float32_t3(pos[3][0], pos[3][1], pos[3][2]); + positions[4] = hlsl::float32_t3(pos[1][0], pos[1][1], pos[1][2]); + positions[5] = hlsl::float32_t3(pos[4][0], pos[4][1], pos[4][2]); + positions[6] = hlsl::float32_t3(pos[7][0], pos[7][1], pos[7][2]); + positions[7] = hlsl::float32_t3(pos[2][0], pos[2][1], pos[2][2]); + positions[8] = hlsl::float32_t3(pos[4][0], pos[4][1], pos[4][2]); + positions[9] = hlsl::float32_t3(pos[6][0], pos[6][1], pos[6][2]); + positions[10] = hlsl::float32_t3(pos[5][0], pos[5][1], pos[5][2]); + positions[11] = hlsl::float32_t3(pos[7][0], pos[7][1], pos[7][2]); + positions[12] = hlsl::float32_t3(pos[6][0], pos[6][1], pos[6][2]); + positions[13] = hlsl::float32_t3(pos[0][0], pos[0][1], pos[0][2]); + positions[14] = hlsl::float32_t3(pos[3][0], pos[3][1], pos[3][2]); + positions[15] = hlsl::float32_t3(pos[5][0], pos[5][1], pos[5][2]); + positions[16] = hlsl::float32_t3(pos[3][0], pos[3][1], pos[3][2]); + positions[17] = hlsl::float32_t3(pos[2][0], pos[2][1], pos[2][2]); + positions[18] = hlsl::float32_t3(pos[7][0], pos[7][1], pos[7][2]); + positions[19] = hlsl::float32_t3(pos[5][0], pos[5][1], pos[5][2]); + positions[20] = hlsl::float32_t3(pos[0][0], pos[0][1], pos[0][2]); + positions[21] = hlsl::float32_t3(pos[6][0], pos[6][1], pos[6][2]); + positions[22] = hlsl::float32_t3(pos[4][0], pos[4][1], pos[4][2]); + positions[23] = hlsl::float32_t3(pos[1][0], pos[1][1], pos[1][2]); + } - switch (f) + // + { + const hlsl::vector norm[6] = + { + hlsl::vector(0, 0, 127), + hlsl::vector(127, 0, 0), + hlsl::vector(0, 0,-127), + hlsl::vector(-127, 0, 0), + hlsl::vector(0, 127, 0), + hlsl::vector(0,-127, 0) + }; + const hlsl::vector uv[4] = { - case 0: - ptr[v+0].setPos(pos[0].X, pos[0].Y, pos[0].Z); - ptr[v+1].setPos(pos[1].X, pos[1].Y, pos[1].Z); - ptr[v+2].setPos(pos[2].X, pos[2].Y, pos[2].Z); - ptr[v+3].setPos(pos[3].X, pos[3].Y, pos[3].Z); - break; - case 1: - ptr[v+0].setPos(pos[1].X, pos[1].Y, pos[1].Z); - ptr[v+1].setPos(pos[4].X, pos[4].Y, pos[4].Z); - ptr[v+2].setPos(pos[7].X, pos[7].Y, pos[7].Z); - ptr[v+3].setPos(pos[2].X, pos[2].Y, pos[2].Z); - break; - case 2: - ptr[v+0].setPos(pos[4].X, pos[4].Y, pos[4].Z); - ptr[v+1].setPos(pos[6].X, pos[6].Y, pos[6].Z); - ptr[v+2].setPos(pos[5].X, pos[5].Y, pos[5].Z); - ptr[v+3].setPos(pos[7].X, pos[7].Y, pos[7].Z); - break; - case 3: - ptr[v+0].setPos(pos[6].X, pos[6].Y, pos[6].Z); - ptr[v+2].setPos(pos[3].X, pos[3].Y, pos[3].Z); - ptr[v+1].setPos(pos[0].X, pos[0].Y, pos[0].Z); - ptr[v+3].setPos(pos[5].X, pos[5].Y, pos[5].Z); - break; - case 4: - ptr[v+0].setPos(pos[3].X, pos[3].Y, pos[3].Z); - ptr[v+1].setPos(pos[2].X, pos[2].Y, pos[2].Z); - ptr[v+2].setPos(pos[7].X, pos[7].Y, pos[7].Z); - ptr[v+3].setPos(pos[5].X, pos[5].Y, pos[5].Z); - break; - case 5: - ptr[v+0].setPos(pos[0].X, pos[0].Y, pos[0].Z); - ptr[v+1].setPos(pos[6].X, pos[6].Y, pos[6].Z); - ptr[v+2].setPos(pos[4].X, pos[4].Y, pos[4].Z); - ptr[v+3].setPos(pos[1].X, pos[1].Y, pos[1].Z); - break; + hlsl::vector( 0,255), + hlsl::vector(255,255), + hlsl::vector(255, 0), + hlsl::vector( 0, 0) + }; + for (size_t f=0ull; f<6ull; ++f) + { + const size_t v = f*4ull; + + for (size_t i=0ull; i<4ull; ++i) + { + normals[v+i] = vector(norm[f],0); + uvs[v+i] = uv[i]; + } } } - retval.bindings[0] = {0ull,std::move(vertices)}; - - // Recalculate bounding box - retval.indexType = asset::EIT_16BIT; - retval.bbox = core::aabbox3df(-size*0.5f,size*0.5f); + CPolygonGeometryManipulator::recomputeContentHashes(retval.get()); return retval; } +#if 0 /* a cylinder, a cone and a cross point up on (0,1.f, 0.f ) */ -CGeometryCreator::return_type CGeometryCreator::createArrowMesh(const uint32_t tesselationCylinder, - const uint32_t tesselationCone, - const float height, - const float cylinderHeight, - const float width0, - const float width1, - const video::SColor vtxColor0, - const video::SColor vtxColor1, - IMeshManipulator* const meshManipulatorOverride) const +core::smart_refctd_ptr CGeometryCreator::createArrow( + const uint32_t tesselationCylinder, + const uint32_t tesselationCone, + const float height, + const float cylinderHeight, + const float width0, + const float width1, + const video::SColor vtxColor0, + const video::SColor vtxColor1 +) const { assert(height > cylinderHeight); @@ -248,7 +289,7 @@ CGeometryCreator::return_type CGeometryCreator::createArrowMesh(const uint32_t t } /* A sphere with proper normals and texture coords */ -CGeometryCreator::return_type CGeometryCreator::createSphereMesh(float radius, uint32_t polyCountX, uint32_t polyCountY, IMeshManipulator* const meshManipulatorOverride) const +core::smart_refctd_ptr CGeometryCreator::createSphere(float radius, uint32_t polyCountX, uint32_t polyCountY, IMeshManipulator* const meshManipulatorOverride) const { // we are creating the sphere mesh here. return_type retval; @@ -372,7 +413,7 @@ CGeometryCreator::return_type CGeometryCreator::createSphereMesh(float radius, u { // calculate points position - core::vector3df pos(static_cast(cos(axz) * sinay), + float32_t3 pos(static_cast(cos(axz) * sinay), static_cast(cos(ay)), static_cast(sin(axz) * sinay)); // for spheres the normal is the position @@ -435,7 +476,7 @@ CGeometryCreator::return_type CGeometryCreator::createSphereMesh(float radius, u // recalculate bounding box core::aabbox3df BoundingBox; - BoundingBox.reset(core::vector3df(radius)); + BoundingBox.reset(float32_t3(radius)); BoundingBox.addInternalPoint(-radius, -radius, -radius); // set vertex buffer @@ -449,8 +490,10 @@ CGeometryCreator::return_type CGeometryCreator::createSphereMesh(float radius, u } /* A cylinder with proper normals and texture coords */ -CGeometryCreator::return_type CGeometryCreator::createCylinderMesh(float radius, float length, - uint32_t tesselation, const video::SColor& color, IMeshManipulator* const meshManipulatorOverride) const +core::smart_refctd_ptr CGeometryCreator::createCylinder( + float radius, float length, + uint32_t tesselation, const video::SColor& color, IMeshManipulator* const meshManipulatorOverride +) const { return_type retval; constexpr size_t vertexSize = sizeof(CGeometryCreator::CylinderVertex); @@ -519,11 +562,13 @@ CGeometryCreator::return_type CGeometryCreator::createCylinderMesh(float radius, } /* A cone with proper normals and texture coords */ -CGeometryCreator::return_type CGeometryCreator::createConeMesh( float radius, float length, uint32_t tesselation, - const video::SColor& colorTop, - const video::SColor& colorBottom, - float oblique, - IMeshManipulator* const meshManipulatorOverride) const +core::smart_refctd_ptr CGeometryCreator::createCone( + float radius, float length, uint32_t tesselation, + const video::SColor& colorTop, + const video::SColor& colorBottom, + float oblique, + IMeshManipulator* const meshManipulatorOverride +) const { const size_t vtxCnt = tesselation * 2; auto vtxBuf = asset::ICPUBuffer::create({ vtxCnt * sizeof(ConeVertex) }); @@ -604,113 +649,211 @@ CGeometryCreator::return_type CGeometryCreator::createConeMesh( float radius, fl return cone; } +#endif - -CGeometryCreator::return_type CGeometryCreator::createRectangleMesh(const core::vector2df_SIMD& _size) const +core::smart_refctd_ptr CGeometryCreator::createRectangle(const hlsl::float32_t2 size) const { - return_type retval; - constexpr size_t vertexSize = sizeof(CGeometryCreator::RectangleVertex); - retval.inputParams = { 0b1111u,0b1u,{ - {0u,EF_R32G32B32_SFLOAT,offsetof(RectangleVertex,pos)}, - {0u,EF_R8G8B8A8_UNORM,offsetof(RectangleVertex,color)}, - {0u,EF_R8G8_USCALED,offsetof(RectangleVertex,uv)}, - {0u,EF_R32G32B32_SFLOAT,offsetof(RectangleVertex,normal)} - },{vertexSize,SVertexInputBindingParams::EVIR_PER_VERTEX} }; - // Create indices - retval.indexCount = 6; - retval.indexType = asset::EIT_16BIT; - uint16_t u[6]; + using namespace hlsl; - /* - 0---1 - | / | - 3---2 - */ - u[0] = 0; - u[1] = 3; - u[2] = 1; - u[3] = 1; - u[4] = 3; - u[5] = 2; - - auto indices = asset::ICPUBuffer::create({ sizeof(u) }); - memcpy(indices->getPointer(), u, sizeof(u)); - indices->addUsageFlags(asset::IBuffer::EUF_INDEX_BUFFER_BIT); - retval.indexBuffer = { 0ull, std::move(indices) }; + auto retval = core::make_smart_refctd_ptr(); + retval->setIndexing(IPolygonGeometryBase::TriangleList()); + + // Create indices + { + using index_t = uint16_t; + /* + 0---1 + | / | + 3---2 + */ + const index_t indices[] = {0,3,1,1,3,2}; + auto buffer = ICPUBuffer::create({ + {sizeof(indices),IBuffer::EUF_INDEX_BUFFER_BIT}, + const_cast((const void*)indices) // TODO: temporary till two different creation params (adopting needs non const void, copying needs const void only + }); + shapes::AABB<4,index_t> aabb; + aabb.minVx[0] = 0; + aabb.maxVx[0] = 3; + retval->setIndexView({ + .composed = { + .encodedDataRange = {.u16=aabb}, + .stride = sizeof(index_t), + .format = EF_R16_UINT, + .rangeFormat = IGeometryBase::EAABBFormat::U16 + }, + .src = {.offset=0,.size=buffer->getSize(),.buffer=std::move(buffer)} + }); + } // Create vertices - auto vertices = asset::ICPUBuffer::create({ 4 * vertexSize }); - RectangleVertex* ptr = (RectangleVertex*)vertices->getPointer(); - - ptr[0] = RectangleVertex(core::vector3df_SIMD(-1.0f, 1.0f, 0.0f) * _size, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - ptr[1] = RectangleVertex(core::vector3df_SIMD( 1.0f, 1.0f, 0.0f) * _size, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(1u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - ptr[2] = RectangleVertex(core::vector3df_SIMD( 1.0f, -1.0f, 0.0f) * _size, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(1u, 0u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - ptr[3] = RectangleVertex(core::vector3df_SIMD(-1.0f, -1.0f, 0.0f) * _size, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 0u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - - vertices->addUsageFlags(asset::IBuffer::EUF_VERTEX_BUFFER_BIT); - retval.bindings[0] = {0ull, std::move(vertices)}; + { + { + const hlsl::float32_t2 positions[] = { + hlsl::float32_t2(-size.x, size.y), + hlsl::float32_t2( size.x, size.y), + hlsl::float32_t2( size.x,-size.y), + hlsl::float32_t2(-size.x,-size.y) + }; + auto buff = ICPUBuffer::create({{sizeof(positions),IBuffer::EUF_NONE},(void*)positions}); + shapes::AABB<4,float32_t> aabb; + aabb.minVx = float32_t4(-size,0.f,0.f); + aabb.maxVx = float32_t4( size,0.f,0.f); + retval->setPositionView({ + .composed = { + .encodedDataRange = {.f32=aabb}, + .stride = sizeof(positions[0]), + .format = EF_R32G32_SFLOAT, + .rangeFormat = IGeometryBase::EAABBFormat::F32 + }, + .src = {.offset=0,.size=buff->getSize(),.buffer = std::move(buff)} + }); + } + { + const hlsl::vector normals[] = { + hlsl::vector(0,0,127,0), + hlsl::vector(0,0,127,0), + hlsl::vector(0,0,127,0), + hlsl::vector(0,0,127,0) + }; + auto buff = ICPUBuffer::create({{sizeof(normals),IBuffer::EUF_NONE},(void*)normals}); + shapes::AABB<4,int8_t> aabb; + aabb.maxVx = hlsl::vector(0,0,127,0); + aabb.minVx = -aabb.maxVx; + retval->setNormalView({ + .composed = { + .encodedDataRange = {.s8=aabb}, + .stride = sizeof(normals[0]), + .format = EF_R8G8B8A8_SNORM, + .rangeFormat = IGeometryBase::EAABBFormat::S8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + { + const hlsl::vector uvs[] = { + hlsl::vector( 0,255), + hlsl::vector(255,255), + hlsl::vector(255, 0), + hlsl::vector( 0, 0) + }; + auto buff = ICPUBuffer::create({{sizeof(uvs),IBuffer::EUF_NONE},(void*)uvs}); + shapes::AABB<4,uint8_t> aabb; + aabb.minVx = hlsl::vector(0,0,0,0); + aabb.maxVx = hlsl::vector(255,255,0,0); + retval->getAuxAttributeViews()->push_back({ + .composed = { + .encodedDataRange = {.u8=aabb}, + .stride = sizeof(uvs[0]), + .format = EF_R8G8_UNORM, + .rangeFormat = IGeometryBase::EAABBFormat::U8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + } + CPolygonGeometryManipulator::recomputeContentHashes(retval.get()); return retval; } -CGeometryCreator::return_type CGeometryCreator::createDiskMesh(float radius, uint32_t tesselation) const +core::smart_refctd_ptr CGeometryCreator::createDisk(const float radius, const uint32_t tesselation) const { - return_type retval; - constexpr size_t vertexSize = sizeof(CGeometryCreator::DiskVertex); + // need at least 120 external angles in the fan + if (tesselation<2) + return nullptr; - retval.inputParams = { 0b1111u,0b1u,{ - {0u,EF_R32G32B32_SFLOAT,offsetof(DiskVertex,pos)}, - {0u,EF_R8G8B8A8_UNORM,offsetof(DiskVertex,color)}, - {0u,EF_R8G8_USCALED,offsetof(DiskVertex,uv)}, - {0u,EF_R32G32B32_SFLOAT,offsetof(DiskVertex,normal)} - },{vertexSize,SVertexInputBindingParams::EVIR_PER_VERTEX} }; - retval.assemblyParams.primitiveType = EPT_TRIANGLE_FAN; // without indices - retval.indexType = EIT_UNKNOWN; + using namespace hlsl; - const size_t vertexCount = 2u + tesselation; - retval.indexCount = vertexCount; - - const float angle = 360.0f / static_cast(tesselation); - - auto vertices = asset::ICPUBuffer::create({ vertexCount * vertexSize }); - DiskVertex* ptr = (DiskVertex*)vertices->getPointer(); - - const core::vectorSIMDf v0(0.0f, radius, 0.0f, 1.0f); - core::matrix3x4SIMD rotation; - - //center - ptr[0] = DiskVertex(core::vector3df_SIMD(0.0f), video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); + auto retval = core::make_smart_refctd_ptr(); + retval->setIndexing(IPolygonGeometryBase::TriangleFan()); - //v0 - ptr[1] = DiskVertex(v0, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); - - //vn - ptr[vertexCount - 1] = ptr[1]; + // without index buffer + const size_t vertexCount = 2u + tesselation; - //v1, v2, ..., vn-1 - for (int i = 2; i < vertexCount-1; i++) + float32_t2* positions; + // for now because no reliable RGB10A2 encode and scant support for 24-bit UTB formats + hlsl::vector* normals; + // + constexpr uint16_t UnityUV = 0xffffu; + uint16_t2* uvs; { - core::vectorSIMDf vn; - core::matrix3x4SIMD rotMatrix; - rotMatrix.setRotation(core::quaternion(0.0f, 0.0f, core::radians((i-1)*angle))); - rotMatrix.transformVect(vn, v0); - - ptr[i] = DiskVertex(vn, video::SColor(0xFFFFFFFFu), - core::vector2du32_SIMD(0u, 1u), core::vector3df_SIMD(0.0f, 0.0f, 1.0f)); + { + constexpr auto AttrSize = sizeof(decltype(*positions)); + auto buff = ICPUBuffer::create({AttrSize*vertexCount,IBuffer::EUF_NONE}); + positions = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,float32_t> aabb; + aabb.maxVx = float32_t4(radius,radius,0.f,0.f); + aabb.minVx = -aabb.maxVx; + retval->setPositionView({ + .composed = { + .encodedDataRange = {.f32=aabb}, + .stride = AttrSize, + .format = EF_R32G32_SFLOAT, + .rangeFormat = IGeometryBase::EAABBFormat::F32 + }, + .src = {.offset=0,.size=buff->getSize(),.buffer = std::move(buff)} + }); + } + { + constexpr auto AttrSize = sizeof(decltype(*normals)); + auto buff = ICPUBuffer::create({AttrSize*vertexCount,IBuffer::EUF_NONE}); + normals = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,int8_t> aabb; + aabb.maxVx = hlsl::vector(0,0,127,0); + aabb.minVx = -aabb.maxVx; + retval->setNormalView({ + .composed = { + .encodedDataRange = {.s8=aabb}, + .stride = AttrSize, + .format = EF_R8G8B8A8_SNORM, + .rangeFormat = IGeometryBase::EAABBFormat::S8_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } + { + constexpr auto AttrSize = sizeof(decltype(*uvs)); + auto buff = ICPUBuffer::create({AttrSize*vertexCount,IBuffer::EUF_NONE}); + uvs = reinterpret_cast(buff->getPointer()); + shapes::AABB<4,uint16_t> aabb; + aabb.minVx = uint16_t4(0,0,0,0); + aabb.maxVx = uint16_t4(UnityUV,UnityUV,0,0); + retval->getAuxAttributeViews()->push_back({ + .composed = { + .encodedDataRange = {.u16=aabb}, + .stride = AttrSize, + .format = EF_R16G16_UNORM, + .rangeFormat = IGeometryBase::EAABBFormat::U16_NORM + }, + .src = {.offset=0,.size=buff->getSize(),.buffer=std::move(buff)} + }); + } } - vertices->addUsageFlags(asset::IBuffer::EUF_VERTEX_BUFFER_BIT); - retval.bindings[0] = {0ull, std::move(vertices)}; + // populate data + { + const float angle = 360.f / static_cast(tesselation); + // center + *(positions++) = float32_t2(0.f,0.f); + *(uvs++) = uint16_t2(0,UnityUV); + // last + positions[tesselation] = float32_t3(0.f,radius,0.f); + uvs[tesselation] = uint16_t2(UnityUV,0); + for (auto i=0; i; + *(positions++) = float32_t2(hlsl::sin(rad),hlsl::cos(rad))*radius; + *(uvs++) = uint16_t2(t*UnityUV+0.5f,0); + } + } + std::fill_n(normals,vertexCount,hlsl::vector(0,0,127,0)); + CPolygonGeometryManipulator::recomputeContentHashes(retval.get()); return retval; } +#if 0 /* Helpful Icosphere class implementation used to compute and create icopshere's vertices and indecies. @@ -1675,7 +1818,7 @@ class Icosphere }; -CGeometryCreator::return_type CGeometryCreator::createIcoSphere(float radius, uint32_t subdivision, bool smooth) const +core::smart_refctd_ptr CGeometryCreator::createIcoSphere(float radius, uint32_t subdivision, bool smooth) const { Icosphere IcosphereData(radius, subdivision, smooth); @@ -1708,7 +1851,7 @@ CGeometryCreator::return_type CGeometryCreator::createIcoSphere(float radius, ui return icosphereGeometry; } - +#endif } // end namespace nbl::asset diff --git a/src/nbl/asset/utils/CGeometryCreator.h b/src/nbl/asset/utils/CGeometryCreator.h deleted file mode 100644 index 50ddcb646c..0000000000 --- a/src/nbl/asset/utils/CGeometryCreator.h +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_GEOMETRY_CREATOR_H_INCLUDED__ -#define __NBL_ASSET_C_GEOMETRY_CREATOR_H_INCLUDED__ - -#include "nbl/asset/utils/IGeometryCreator.h" - -namespace nbl -{ - -namespace asset -{ - -//! class for creating geometry on the fly -class CGeometryCreator : public IGeometryCreator -{ - public: - #include "nbl/nblpack.h" - struct CubeVertex - { - float pos[3]; - uint8_t color[4]; // normalized - uint8_t uv[2]; - int8_t normal[3]; - uint8_t dummy[3]; - - void setPos(float x, float y, float z) { pos[0] = x; pos[1] = y; pos[2] = z; } - void translate(float dx, float dy, float dz) { pos[0] += dx; pos[1] += dy; pos[2] += dz; } - void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { color[0] = r; color[1] = g; color[2] = b; color[3] = a; } - void setNormal(int8_t x, int8_t y, int8_t z) { normal[0] = x; normal[1] = y; normal[2] = z; } - void setUv(uint8_t u, uint8_t v) { uv[0] = u; uv[1] = v; } - } PACK_STRUCT; - - public: - CGeometryCreator(IMeshManipulator* const _defaultMeshManipulator); - - private: - struct RectangleVertex - { - RectangleVertex(const core::vector3df_SIMD& _pos, const video::SColor& _color, const core::vector2du32_SIMD _uv, const core::vector3df_SIMD _normal) - { - memcpy(pos, _pos.pointer, sizeof(float) * 3); - _color.toOpenGLColor(color); - uv[0] = _uv.x; - uv[1] = _uv.y; - normal[0] = _normal.x; - normal[1] = _normal.y; - normal[2] = _normal.z; - } - float pos[3]; - uint8_t color[4]; - uint8_t uv[2]; - uint8_t dummy[2]; - float normal[3]; - } PACK_STRUCT; - - typedef RectangleVertex DiskVertex; - - struct ConeVertex - { - inline ConeVertex(const core::vectorSIMDf& _pos, const CQuantNormalCache::value_type_t& _nml, const video::SColor& _color) : normal{_nml} - { - memcpy(pos, _pos.pointer, 12); - _color.toOpenGLColor(color); - } - - float pos[3]; - uint8_t color[4]; - CQuantNormalCache::value_type_t normal; - } PACK_STRUCT; - - struct CylinderVertex - { - CylinderVertex() : pos{0.f, 0.f, 0.f}, color{0u, 0u, 0u, 0u}, uv{0.f, 0.f}, normal() {} - - float pos[3]; - uint8_t color[4]; - float uv[2]; - CQuantNormalCache::value_type_t normal; - } PACK_STRUCT; - - struct IcosphereVertex - { - IcosphereVertex() : pos{ 0.f, 0.f, 0.f }, normals{ 0.f, 0.f, 0.f }, uv{ 0.f, 0.f } {} - - float pos[3]; - float normals[3]; - float uv[2]; - } PACK_STRUCT; - #include "nbl/nblunpack.h" - - - using SphereVertex = CylinderVertex; - using ArrowVertex = CylinderVertex; - - //smart_refctd_ptr? - IMeshManipulator* const defaultMeshManipulator; - - public: - return_type createCubeMesh(const core::vector3df& size) const override; - - return_type createArrowMesh(const uint32_t tesselationCylinder, - const uint32_t tesselationCone, const float height, - const float cylinderHeight, const float width0, - const float width1, const video::SColor vtxColor0, - const video::SColor vtxColor1, - IMeshManipulator* const meshManipulatorOverride = nullptr) const override; - - return_type createSphereMesh(float radius, uint32_t polyCountX, uint32_t polyCountY, IMeshManipulator* const meshManipulatorOverride = nullptr) const override; - - return_type createCylinderMesh( float radius, float length, uint32_t tesselation, - const video::SColor& color=0xffffffff, - IMeshManipulator* const meshManipulatorOverride = nullptr) const override; - - return_type createConeMesh( float radius, float length, uint32_t tesselation, - const video::SColor& colorTop=0xffffffff, - const video::SColor& colorBottom=0xffffffff, - float oblique=0.f, - IMeshManipulator* const meshManipulatorOverride = nullptr) const override; - - return_type createRectangleMesh(const core::vector2df_SIMD& _size = core::vector2df_SIMD(0.5f, 0.5f)) const override; - - return_type createDiskMesh(float radius, uint32_t tesselation) const override; - - return_type createIcoSphere(float radius = 1.0f, uint32_t subdivision = 1, bool smooth = false) const override; - -}; - -} // end namespace asset -} // end namespace nbl - -#endif - diff --git a/src/nbl/asset/utils/CMeshManipulator.h b/src/nbl/asset/utils/CMeshManipulator.h deleted file mode 100644 index 7a5b895665..0000000000 --- a/src/nbl/asset/utils/CMeshManipulator.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_ASSET_C_MESH_MANIPULATOR_H_INCLUDED__ -#define __NBL_ASSET_C_MESH_MANIPULATOR_H_INCLUDED__ - -#include "nbl/asset/utils/IMeshManipulator.h" -#include "nbl/asset/utils/CQuantNormalCache.h" - -namespace nbl -{ -namespace asset -{ - -//! An interface for easy manipulation of meshes. -/** Scale, set alpha value, flip surfaces, and so on. This exists for fixing -problems with wrong imported or exported meshes quickly after loading. It is -not intended for doing mesh modifications and/or animations during runtime. -*/ -class CMeshManipulator : public IMeshManipulator -{ - struct SAttrib - { - E_FORMAT type; - E_FORMAT prevType; - size_t size; - uint32_t vaid; - size_t offset; - - SAttrib() : type(EF_UNKNOWN), size(0), vaid(ICPUMeshBuffer::MAX_VERTEX_ATTRIB_COUNT) {} - - friend bool operator>(const SAttrib& _a, const SAttrib& _b) { return _a.size > _b.size; } - }; - struct SAttribTypeChoice - { - E_FORMAT type; - }; - - public: - static core::smart_refctd_ptr createMeshBufferFetchOptimized(const ICPUMeshBuffer* _inbuffer); - - CQuantNormalCache* getQuantNormalCache() override { return &quantNormalCache; } - CQuantQuaternionCache* getQuantQuaternionCache() override { return &quantQuaternionCache; } - - private: - friend class IMeshManipulator; - - template - static void _filterInvalidTriangles(ICPUMeshBuffer* _input); - - //! Meant to create 32bit index buffer from subrange of index buffer containing 16bit indices. Remember to set to index buffer offset to 0 after mapping buffer resulting from this function. - static inline core::smart_refctd_ptr create32BitFrom16BitIdxBufferSubrange(const uint16_t* _in, uint32_t _idxCount) - { - if (!_in) - return nullptr; - - auto out = ICPUBuffer::create({ sizeof(uint32_t) * _idxCount }); - - auto* outPtr = reinterpret_cast(out->getPointer()); - - for (uint32_t i=0u; i<_idxCount; ++i) - outPtr[i] = _in[i]; - - return out; - } - - static core::vector findBetterFormatF(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric, CQuantNormalCache& _cache); - - struct SIntegerAttr - { - uint32_t pointer[4]; - }; - static core::vector findBetterFormatI(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric); - - //E_COMPONENT_TYPE getBestTypeF(bool _normalized, E_COMPONENTS_PER_ATTRIBUTE _cpa, size_t* _outSize, E_COMPONENTS_PER_ATTRIBUTE* _outCpa, const float* _min, const float* _max) const; - static E_FORMAT getBestTypeI(E_FORMAT _originalType, size_t* _outSize, const uint32_t* _min, const uint32_t* _max); - static core::vector findTypesOfProperRangeF(E_FORMAT _type, size_t _sizeThreshold, const float* _min, const float* _max, const SErrorMetric& _errMetric); - - //! Calculates quantization errors and compares them with given epsilon. - /** @returns false when first of calculated errors goes above epsilon or true if reached end without such. */ - static bool calcMaxQuantizationError(const SAttribTypeChoice& _srcType, const SAttribTypeChoice& _dstType, const core::vector& _data, const SErrorMetric& _errMetric, CQuantNormalCache& _cache); - - template - static inline core::smart_refctd_ptr lineStripsToLines(const void* _input, uint32_t& _idxCount) - { - const auto outputSize = _idxCount = (_idxCount - 1) * 2; - - auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); - const auto* iptr = reinterpret_cast(_input); - auto* optr = reinterpret_cast(output->getPointer()); - for (uint32_t i = 0, j = 0; i < outputSize;) - { - optr[i++] = iptr[j++]; - optr[i++] = iptr[j]; - } - return output; - } - - template - static inline core::smart_refctd_ptr triangleStripsToTriangles(const void* _input, uint32_t& _idxCount) - { - const auto outputSize = _idxCount = (_idxCount - 2) * 3; - - auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); - const auto* iptr = reinterpret_cast(_input); - auto* optr = reinterpret_cast(output->getPointer()); - for (uint32_t i = 0, j = 0; i < outputSize; j += 2) - { - optr[i++] = iptr[j + 0]; - optr[i++] = iptr[j + 1]; - optr[i++] = iptr[j + 2]; - if (i == outputSize) - break; - optr[i++] = iptr[j + 2]; - optr[i++] = iptr[j + 1]; - optr[i++] = iptr[j + 3]; - } - return output; - } - - template - static inline core::smart_refctd_ptr trianglesFanToTriangles(const void* _input, uint32_t& _idxCount) - { - const auto outputSize = _idxCount = (_idxCount - 2) * 3; - - auto output = ICPUBuffer::create({ sizeof(OutType)*outputSize }); - const auto* iptr = reinterpret_cast(_input); - auto* optr = reinterpret_cast(output->getPointer()); - for (uint32_t i = 0, j = 1; i < outputSize;) - { - optr[i++] = iptr[0]; - optr[i++] = iptr[j++]; - optr[i++] = iptr[j]; - } - return output; - } - - private: - CQuantNormalCache quantNormalCache; - CQuantQuaternionCache quantQuaternionCache; -}; - -} // end namespace scene -} // end namespace nbl - - -#endif diff --git a/src/nbl/asset/utils/COverdrawMeshOptimizer.cpp b/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.cpp similarity index 98% rename from src/nbl/asset/utils/COverdrawMeshOptimizer.cpp rename to src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.cpp index 1929437279..3b140ff460 100644 --- a/src/nbl/asset/utils/COverdrawMeshOptimizer.cpp +++ b/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.cpp @@ -1,19 +1,17 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#include "nbl/core/declarations.h" -#include "COverdrawMeshOptimizer.h" +#include "nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h" #include #include -#include "CMeshManipulator.h" namespace nbl::asset { - +#if 0 void COverdrawMeshOptimizer::createOptimized(asset::ICPUMeshBuffer* _outbuffer, const asset::ICPUMeshBuffer* _inbuffer, float _threshold, const system::logger_opt_ptr logger) { if (!_outbuffer || !_inbuffer) @@ -267,5 +265,5 @@ size_t COverdrawMeshOptimizer::updateCache(uint32_t _a, uint32_t _b, uint32_t _c return cacheMisses; } - +#endif } // nbl::asset diff --git a/src/nbl/asset/utils/COverdrawMeshOptimizer.h b/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h similarity index 77% rename from src/nbl/asset/utils/COverdrawMeshOptimizer.h rename to src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h index bf5ac15e84..9e751b6493 100644 --- a/src/nbl/asset/utils/COverdrawMeshOptimizer.h +++ b/src/nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h @@ -1,20 +1,17 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_OVERDRAW_POLYGON_GEOMETRY_OPTIMIZER_H_INCLUDED_ +#define _NBL_ASSET_C_OVERDRAW_POLYGON_GEOMETRY_OPTIMIZER_H_INCLUDED_ -#ifndef __NBL_ASSET_C_OVERDRAW_MESH_OPTIMIZER_H_INCLUDED__ -#define __NBL_ASSET_C_OVERDRAW_MESH_OPTIMIZER_H_INCLUDED__ -#include "nbl/asset/ICPUMeshBuffer.h" +#include "nbl/asset/ICPUPolygonGeometry.h" -// Based on zeux's meshoptimizer (https://github.com/zeux/meshoptimizer) available under MIT license -namespace nbl -{ -namespace asset +namespace nbl::asset { - -class COverdrawMeshOptimizer +// Based on zeux's meshoptimizer (https://github.com/zeux/meshoptimizer) available under MIT license +class COverdrawPolygonGeometryOptimizer { _NBL_STATIC_INLINE_CONSTEXPR size_t CACHE_SIZE = 16; @@ -31,7 +28,7 @@ class COverdrawMeshOptimizer }; // private, undefined constructor - COverdrawMeshOptimizer() = delete; + COverdrawPolygonGeometryOptimizer() = delete; public: //! Creates new or modifies given mesh reordering indices to reduce pixel overdraw and vertex shader invocations. @@ -40,7 +37,7 @@ class COverdrawMeshOptimizer @param _createNew Flag deciding whether to create new mesh (not modifying given one) or just optimize given mesh. Defaulted to true (i.e. create new). @param _threshold Indicates how much the overdraw optimizer can degrade vertex cache efficiency (1.05 = up to 5%) to reduce overdraw more efficiently. Defaulted to 1.05 (i.e. 5%). */ - static void createOptimized(asset::ICPUMeshBuffer* _outbuffer, const asset::ICPUMeshBuffer* _inbuffer, float _threshold = 1.05f, const system::logger_opt_ptr logger = nullptr); + static void createOptimized(ICPUPolygonGeometry* out, const ICPUPolygonGeometry* in, float _threshold = 1.05f, const system::logger_opt_ptr logger = nullptr); private: template @@ -55,6 +52,4 @@ class COverdrawMeshOptimizer }; } -} - #endif \ No newline at end of file diff --git a/src/nbl/asset/utils/CMeshManipulator.cpp b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp similarity index 99% rename from src/nbl/asset/utils/CMeshManipulator.cpp rename to src/nbl/asset/utils/CPolygonGeometryManipulator.cpp index d06762bf86..1e08c172ba 100644 --- a/src/nbl/asset/utils/CMeshManipulator.cpp +++ b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp @@ -1,7 +1,10 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h + +#include "nbl/asset/asset.h" + #include #include #include @@ -9,17 +12,15 @@ #include #include - -#include "nbl/asset/asset.h" -#include "nbl/asset/IRenderpassIndependentPipeline.h" -#include "nbl/asset/utils/CMeshManipulator.h" +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" #include "nbl/asset/utils/CSmoothNormalGenerator.h" #include "nbl/asset/utils/CForsythVertexCacheOptimizer.h" -#include "nbl/asset/utils/COverdrawMeshOptimizer.h" +#include "nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h" + namespace nbl::asset { - +#if 0 //! Flips the direction of surfaces. Changes backfacing triangles to frontfacing //! triangles and vice versa. //! \param mesh: Mesh on which the operation is performed. @@ -1724,6 +1725,7 @@ core::matrix3x4SIMD IMeshManipulator::calculateOBB(const nbl::asset::ICPUMeshBuf return TransMat; } +#endif } // end namespace nbl::asset diff --git a/src/nbl/asset/utils/CSmoothNormalGenerator.cpp b/src/nbl/asset/utils/CSmoothNormalGenerator.cpp index 6dcb7ff69f..72c2a3acc8 100644 --- a/src/nbl/asset/utils/CSmoothNormalGenerator.cpp +++ b/src/nbl/asset/utils/CSmoothNormalGenerator.cpp @@ -14,7 +14,7 @@ namespace nbl { namespace asset { - +#if 0 static inline bool operator<(uint32_t lhs, const IMeshManipulator::SSNGVertexData& rhs) { return lhs < rhs.hash; @@ -281,6 +281,6 @@ std::array CSmoothNormalGenerator::VertexHashMap::getNeighboringCel } return neighbourhood; } - +#endif } } \ No newline at end of file diff --git a/src/nbl/asset/utils/CSmoothNormalGenerator.h b/src/nbl/asset/utils/CSmoothNormalGenerator.h index 1181a9011f..f921c4cea8 100644 --- a/src/nbl/asset/utils/CSmoothNormalGenerator.h +++ b/src/nbl/asset/utils/CSmoothNormalGenerator.h @@ -1,82 +1,73 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_C_SMOOTH_NORMAL_GENERATOR_H_INCLUDED_ +#define _NBL_ASSET_C_SMOOTH_NORMAL_GENERATOR_H_INCLUDED_ -#ifndef __NBL_ASSET_C_SMOOTH_NORMAL_GENERATOR_H_INCLUDED__ -#define __NBL_ASSET_C_SMOOTH_NORMAL_GENERATOR_H_INCLUDED__ +#include "nbl/asset/utils/CPolygonGeometryManipulator.h" -#include #include -#include "nbl/core/math/glslFunctions.h" -#include "nbl/asset/ICPUMeshBuffer.h" -#include "nbl/asset/utils/IMeshManipulator.h" - - -namespace nbl -{ -namespace asset +namespace nbl::asset { class CSmoothNormalGenerator { -public: - static core::smart_refctd_ptr calculateNormals(asset::ICPUMeshBuffer* buffer, float epsilon, uint32_t normalAttrID, IMeshManipulator::VxCmpFunction function); - - CSmoothNormalGenerator() = delete; - ~CSmoothNormalGenerator() = delete; - -private: - class VertexHashMap - { public: - struct BucketBounds - { - core::vector::iterator begin; - core::vector::iterator end; - }; + CSmoothNormalGenerator() = delete; + ~CSmoothNormalGenerator() = delete; +#if 0 + static core::smart_refctd_ptr calculateNormals(ICPUMeshBuffer* buffer, float epsilon, uint32_t normalAttrID, IMeshManipulator::VxCmpFunction function); - public: - VertexHashMap(size_t _vertexCount, uint32_t _hashTableMaxSize, float _cellSize); + private: + class VertexHashMap + { + public: + struct BucketBounds + { + core::vector::iterator begin; + core::vector::iterator end; + }; - //inserts vertex into hash table - void add(IMeshManipulator::SSNGVertexData&& vertex); + public: + VertexHashMap(size_t _vertexCount, uint32_t _hashTableMaxSize, float _cellSize); - //sorts hashtable and sets iterators at beginnings of bucktes - void validate(); + //inserts vertex into hash table + void add(IMeshManipulator::SSNGVertexData&& vertex); - // - std::array getNeighboringCellHashes(const IMeshManipulator::SSNGVertexData& vertex); + //sorts hashtable and sets iterators at beginnings of bucktes + void validate(); - inline uint32_t getBucketCount() const { return buckets.size(); } - inline BucketBounds getBucketBoundsById(uint32_t index) { return { buckets[index], buckets[index + 1] }; } - BucketBounds getBucketBoundsByHash(uint32_t hash); + // + std::array getNeighboringCellHashes(const IMeshManipulator::SSNGVertexData& vertex); - private: - static constexpr uint32_t invalidHash = 0xFFFFFFFF; + inline uint32_t getBucketCount() const { return buckets.size(); } + inline BucketBounds getBucketBoundsById(uint32_t index) { return { buckets[index], buckets[index + 1] }; } + BucketBounds getBucketBoundsByHash(uint32_t hash); - private: - //holds iterators pointing to beginning of each bucket, last iterator points to vertices.end() - core::vector::iterator> buckets; - core::vector vertices; - const uint32_t hashTableMaxSize; - const float cellSize; + private: + static constexpr uint32_t invalidHash = 0xFFFFFFFF; - private: - uint32_t hash(const IMeshManipulator::SSNGVertexData& vertex) const; - uint32_t hash(const core::vector3du32_SIMD& position) const; + private: + //holds iterators pointing to beginning of each bucket, last iterator points to vertices.end() + core::vector::iterator> buckets; + core::vector vertices; + const uint32_t hashTableMaxSize; + const float cellSize; - }; + private: + uint32_t hash(const IMeshManipulator::SSNGVertexData& vertex) const; + uint32_t hash(const core::vector3du32_SIMD& position) const; -private: - static VertexHashMap setupData(const asset::ICPUMeshBuffer* buffer, float epsilon); - static void processConnectedVertices(asset::ICPUMeshBuffer* buffer, VertexHashMap& vertices, float epsilon, uint32_t normalAttrID, IMeshManipulator::VxCmpFunction vxcmp); + }; + private: + static VertexHashMap setupData(const ICPUMeshBuffer* buffer, float epsilon); + static void processConnectedVertices(ICPUMeshBuffer* buffer, VertexHashMap& vertices, float epsilon, uint32_t normalAttrID, IMeshManipulator::VxCmpFunction vxcmp); +#endif }; } -} - #endif \ No newline at end of file diff --git a/src/nbl/video/IPhysicalDevice.cpp b/src/nbl/video/IPhysicalDevice.cpp index ded519c1ca..dca2a47289 100644 --- a/src/nbl/video/IPhysicalDevice.cpp +++ b/src/nbl/video/IPhysicalDevice.cpp @@ -417,6 +417,9 @@ asset::E_FORMAT IPhysicalDevice::promoteBufferFormat(const SBufferFormatPromotio asset::E_FORMAT IPhysicalDevice::promoteImageFormat(const SImageFormatPromotionRequest req, const IGPUImage::TILING tiling) const { + if (req.originalFormat==asset::EF_UNKNOWN) + return req.originalFormat; + format_image_cache_t& cache = tiling==IGPUImage::TILING::LINEAR ? this->m_formatPromotionCache.linearTilingImages : this->m_formatPromotionCache.optimalTilingImages; diff --git a/src/nbl/video/utilities/CAssetConverter.cpp b/src/nbl/video/utilities/CAssetConverter.cpp index 72807c7739..3ce684e0b7 100644 --- a/src/nbl/video/utilities/CAssetConverter.cpp +++ b/src/nbl/video/utilities/CAssetConverter.cpp @@ -348,6 +348,14 @@ bool CAssetConverter::patch_impl_t::valid(const ILogicalDevi return !invalid; } +CAssetConverter::patch_impl_t::patch_impl_t(const ICPUPolygonGeometry* view) {} +bool CAssetConverter::patch_impl_t::valid(const ILogicalDevice* device) +{ + // patch only stores usages to propagate, the buffers themselves will check their patches + return true; +} + + // not sure if useful enough to move to core utils template struct index_of; @@ -377,6 +385,17 @@ struct instance_t size_t uniqueCopyGroupID = 0xdeadbeefBADC0FFEull; }; +enum class EPolygonGeometryViewType : uint8_t +{ + Position = 0, + Index = 1, + Normal = 2, + JointOBB = 3, + JointIndices = 4, + JointWeights = 5, + Aux = 6 +}; + // template class AssetVisitor : public CRTP @@ -675,6 +694,47 @@ class AssetVisitor : public CRTP } return true; } + inline bool impl(const instance_t& instance, const CAssetConverter::patch_t& userPatch) + { + const auto* geo = instance.asset; + if (!geo->valid()) + return false; + auto visit = [&](const IGeometry::SDataView& view, const core::bitflag& extraUsages, const EPolygonGeometryViewType type, const uint32_t index=0)->bool + { + if (!view) + return true; + const auto* dep = view.src.buffer.get(); + CAssetConverter::patch_t patch = {dep}; + patch.usage |= extraUsages; + return descend(dep,std::move(patch),type,index); + }; + if (!visit(geo->getPositionView(),userPatch.positionBufferUsages,EPolygonGeometryViewType::Position)) + return false; + if (!visit(geo->getIndexView(),userPatch.indexBufferUsages,EPolygonGeometryViewType::Index)) + return false; + if (!visit(geo->getNormalView(),userPatch.otherBufferUsages,EPolygonGeometryViewType::Normal)) + return false; + if (geo->isSkinned()) + { + if (geo->getJointOBBView()) + if (!visit(*geo->getJointOBBView(),userPatch.otherBufferUsages,EPolygonGeometryViewType::JointOBB)) + return false; + auto i=0; + for (const auto& entry : geo->getJointWeightViews()) + { + if (!visit(entry.indices,userPatch.otherBufferUsages,EPolygonGeometryViewType::JointIndices,i)) + return false; + if (!visit(entry.weights,userPatch.otherBufferUsages,EPolygonGeometryViewType::JointWeights,i)) + return false; + i++; + } + } + auto i = 0; + for (const auto& entry : geo->getAuxAttributeViews()) + if (!visit(entry,userPatch.otherBufferUsages,EPolygonGeometryViewType::Aux,i++)) + return false; + return true; + } }; @@ -995,6 +1055,7 @@ class PatchOverride final : public CAssetConverter::CHashCache::IPatchOverride inline const patch_t* operator()(const lookup_t& lookup) const override {return impl(lookup);} inline const patch_t* operator()(const lookup_t& lookup) const override {return impl(lookup);} inline const patch_t* operator()(const lookup_t& lookup) const override {return impl(lookup);} + inline const patch_t* operator()(const lookup_t& lookup) const override {return impl(lookup);} }; template @@ -1534,6 +1595,60 @@ bool CAssetConverter::CHashCache::hash_impl::operator()(lookup_t lookup) +{ + // NOTE: We don't hash the usage metada from the patch! Because it doesn't matter (applied on buffers). + // The view usage in the patch helps us propagate and patch during DFS, but no more. + const auto* asset = lookup.asset; + // we need to hash stuff that will let us disambiguate having views get visited in same order but from different slots + { + const auto* indexing = asset->getIndexingCallback(); + // the particular callback should be a static and we can rely on the pointer being unique for a particular indexing algorithm + hasher << ptrdiff_t(indexing); + } + // not hashing redundant things (which can be worked out from properties we hash) like AABB, Primitive Count, etc. + auto hashView = [&](const IGeometry::SDataView& view)->void + { + if (!view) + { + hasher << false; + return; + } + hasher << true; + hasher << view.composed; + hasher << view.src.offset; + hasher << view.src.actualSize(); + // don't hash the buffer itself, will get hashed by `visitor` below + }; + hashView(asset->getPositionView()); + hashView(asset->getIndexView()); + hashView(asset->getNormalView()); + hasher << asset->getJointCount(); + if (asset->isSkinned()) + { + if (asset->getJointOBBView()) + hashView(*asset->getJointOBBView()); + hasher << asset->getJointWeightViews().size(); + for (const auto& entry : asset->getJointWeightViews()) + { + hashView(entry.indices); + hashView(entry.weights); + } + } + hasher << asset->getAuxAttributeViews().size(); + for (const auto& entry : asset->getAuxAttributeViews()) + hashView(entry); + + AssetVisitor> visitor = { + *this, + {asset,static_cast(patchOverride)->uniqueCopyGroupID}, + *lookup.patch + }; + if (!visitor()) + return false; + return true; +} + void CAssetConverter::CHashCache::eraseStale(const IPatchOverride* patchOverride) { @@ -1572,6 +1687,7 @@ void CAssetConverter::CHashCache::eraseStale(const IPatchOverride* patchOverride rehash.template operator()(); rehash.template operator()(); // rehash.template operator()(); + rehash.template operator()(); } @@ -1934,6 +2050,86 @@ class GetDependantVisit : public GetDependantVisitBase +class GetDependantVisit : public GetDependantVisitBase +{ + public: + bool finalize() + { + if (!creationParams.indexing) + return false; + creationParams.jointWeightViews = jointWeightViews; + creationParams.auxAttributeViews = auxAttributeViews; + return true; + } + + IGPUPolygonGeometry::SCreationParams creationParams = {}; + // has to be public because of aggregate init, but its only for internal usage! + core::vector jointWeightViews = {}; + core::vector auxAttributeViews = {}; + + protected: + bool descend_impl( + const instance_t& user, const CAssetConverter::patch_t& userPatch, + const instance_t& dep, const CAssetConverter::patch_t& soloPatch, + const EPolygonGeometryViewType type, const uint32_t index + ) + { + auto depObj = getDependant(dep,soloPatch); + if (!depObj) + return false; + const auto* asset = user.asset; + switch (type) + { + case EPolygonGeometryViewType::Position: + // obligatory attribute, handle basic setup here too + creationParams.indexing = asset->getIndexingCallback(); + creationParams.jointCount = asset->getJointCount(); + creationParams.positionView = getView(asset->getPositionView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::Index: + creationParams.indexView = getView(asset->getIndexView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::Normal: + creationParams.normalView = getView(asset->getNormalView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::JointOBB: + creationParams.jointOBBView = getView(*asset->getJointOBBView(),std::move(depObj)); + break; + case EPolygonGeometryViewType::JointIndices: + jointWeightViews.resize(index+1); + jointWeightViews[index].indices = getView(asset->getJointWeightViews()[index].indices,std::move(depObj)); + break; + case EPolygonGeometryViewType::JointWeights: + jointWeightViews.resize(index+1); + jointWeightViews[index].weights = getView(asset->getJointWeightViews()[index].weights,std::move(depObj)); + break; + case EPolygonGeometryViewType::Aux: + auxAttributeViews.push_back(getView(asset->getAuxAttributeViews()[index],std::move(depObj))); + break; + default: + return false; + } + // abuse this pointer to signal invalid state + return creationParams.indexing; + } + + private: + IGPUPolygonGeometry::SDataView getView(const ICPUPolygonGeometry::SDataView& orig, core::smart_refctd_ptr&& buff) + { + IGPUPolygonGeometry::SDataView retval = { + .composed = orig.composed, + .src = { + .offset = orig.src.offset, + .size = orig.src.actualSize(), + .buffer = std::move(buff) + } + }; + if (orig && !retval) + creationParams.indexing = nullptr; + return retval; + } +}; // Needed both for reservation and conversion @@ -2405,12 +2601,9 @@ struct conversions_t if (!deferredAllocator->request(output,constrainMask)) return; } - - if constexpr (!std::is_same_v) - { - // set debug names on everything - setDebugName(conv,output->get(),contentHash,uniqueCopyGroupID); - } + // set debug names on everything! + if constexpr (std::is_base_of_v::video_t>) + setDebugName(conv,output->get(),contentHash,uniqueCopyGroupID); } // Since the dfsCache has the original asset pointers as keys, we map in reverse (multiple `instance_t` can map to the same unique content hash and GPU object) @@ -2587,6 +2780,9 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult case ICPUTopLevelAccelerationStructure::AssetType: visit.template operator()(entry); break; + case ICPUPolygonGeometry::AssetType: + visit.template operator()(entry); + break; // these assets have no dependants, should have never been pushed on the stack default: assert(false); @@ -3318,6 +3514,27 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult } } } + if constexpr (std::is_same_v) + { + for (auto& entry : conversionRequests.contentHashToCanonical) + { + const ICPUPolygonGeometry* asset = entry.second.canonicalAsset; + const auto& patch = dfsCache.nodes[entry.second.patchIndex.value].patch; + for (auto i=0ull; i> visitor = { + {visitBase}, + {asset,uniqueCopyGroupID}, + patch + }; + if (!visitor() || !visitor.finalize()) + continue; + conversionRequests.assign(entry.first,entry.second.firstCopyIx,i,IGPUPolygonGeometry::create(std::move(visitor.creationParams))); + } + } + } // clear what we don't need if constexpr (!std::is_base_of_v) @@ -3456,6 +3673,7 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult dedupCreateProp.template operator()(); dedupCreateProp.template operator()(); // dedupCreateProp.template operator()(); + dedupCreateProp.template operator()(); } // write out results @@ -3528,6 +3746,7 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult ); }; // The order these are called is paramount, the Higher Level User needs to die to let go of dependants and make our Garbage Collection work + pruneStaging.template operator()(); // pruneStaging.template operator()(); pruneStaging.template operator()(); pruneStaging.template operator()(); @@ -3619,6 +3838,11 @@ auto CAssetConverter::reserve(const SInputs& inputs) -> SReserveResult return retval; } + +// Maps GPU Object back to the output array index +template +using reverse_map_t = core::unordered_map::video_t*,uint32_t>; + // ISemaphore::future_t CAssetConverter::convert_impl(SReserveResult& reservations, SConvertParams& params) { @@ -3660,16 +3884,20 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul }; // wipe gpu item in staging cache (this may drop it as well if it was made for only a root asset == no users) - core::unordered_map outputReverseMap; - core::for_each_in_tuple(reservations.m_gpuObjects,[&outputReverseMap](const auto& gpuObjects)->void + core::tuple_transform_t outputReverseMaps; + core::for_each_in_tuple(reservations.m_gpuObjects,[&outputReverseMaps](const auto& gpuObjects)->void { uint32_t i = 0; for (const auto& gpuObj : gpuObjects) - outputReverseMap[gpuObj.value.get()] = i++; + { + const auto* ptr = gpuObj.value.get(); + std::get>(outputReverseMaps)[ptr] = i++; + } } ); - auto markFailure = [&reservations,&outputReverseMap,logger](const char* message, smart_refctd_ptr* canonical, typename SReserveResult::staging_cache_t::mapped_type* cacheNode)->void + auto markFailure = [&reservations,&outputReverseMaps,logger](const char* message, smart_refctd_ptr* canonical, typename SReserveResult::staging_cache_t::mapped_type* cacheNode)->void { + auto& outputReverseMap = std::get>(outputReverseMaps); // wipe the smart pointer to the canonical, make sure we release that memory ASAP if no other user is around *canonical = nullptr; // also drop the smart pointer from the output array so failures release memory quickly @@ -3680,7 +3908,14 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul resultOutput[foundIx->second].value = nullptr; outputReverseMap.erase(foundIx); } - logger.log("%s failed for \"%s\"",system::ILogger::ELL_ERROR,message,cacheNode->gpuRef->getObjectDebugName()); + const char* name = "[Debug Name Unknown because not descended from IBackendObject]"; + if constexpr (std::is_base_of_v::video_t>) + name = cacheNode->gpuRef->getObjectDebugName(); + else + { + // TODO: get name from hash in cacheNode->cacheKey + } + logger.log("%s failed for \"%s\"",system::ILogger::ELL_ERROR,message,name); // drop smart pointer cacheNode->gpuRef = nullptr; }; @@ -5233,6 +5468,7 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul if (const auto ix=compactedOwnershipReleaseIndices[i]; ixgetCreationParams().bufferRange; // swap out the conversion result + auto& outputReverseMap = std::get>(outputReverseMaps); const auto foundIx = outputReverseMap.find(srcAS); if (foundIx!=outputReverseMap.end()) { @@ -5385,6 +5621,27 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul } } } + if constexpr (std::is_same_v) + { + depsMissing = missingDependent.template operator()(pGpuObj->getPositionView().src.buffer.get()); + auto checkView = [&](const IGPUPolygonGeometry::SDataView& view) -> void + { + if (depsMissing || !view) + return; + depsMissing = missingDependent.template operator()(view.src.buffer.get()); + }; + if (const auto* view=pGpuObj->getJointOBBView(); view) + checkView(*view); + checkView(pGpuObj->getIndexView()); + checkView(pGpuObj->getNormalView()); + for (const auto& entry : pGpuObj->getJointWeightViews()) + { + checkView(entry.indices); + checkView(entry.weights); + } + for (const auto& entry : pGpuObj->getAuxAttributeViews()) + checkView(entry); + } if (depsMissing) { smart_refctd_ptr dummy; @@ -5400,7 +5657,8 @@ ISemaphore::future_t CAssetConverter::convert_impl(SReserveResul checkDependents.template operator()(); checkDependents.template operator()(); checkDependents.template operator()(); -// mergeCache.template operator()(); +// checkDependents.template operator()(); + checkDependents.template operator()(); // overwrite the compacted TLASes in Descriptor Sets if (auto& tlasRewriteSet=reservations.m_potentialTLASRewrites; !tlasRewriteSet.empty()) { diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 1761ea9f07..57f66ad44b 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -2,5 +2,7 @@ add_subdirectory(nsc) add_subdirectory(xxHash256) if(NBL_BUILD_IMGUI) - add_subdirectory(nite) -endif() \ No newline at end of file + add_subdirectory(nite EXCLUDE_FROM_ALL) +endif() + +NBL_ADJUST_FOLDERS(tools) \ No newline at end of file