diff --git a/.github/workflows/build-nabla.yml b/.github/workflows/build-nabla.yml index 6824449abb..f92ffa7545 100644 --- a/.github/workflows/build-nabla.yml +++ b/.github/workflows/build-nabla.yml @@ -146,11 +146,12 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} result-encoding: string script: | + const headSha = context.payload.pull_request ? context.payload.pull_request.head.sha : context.sha; const response = await github.rest.checks.create({ owner: context.repo.owner, repo: context.repo.repo, name: `Examples (${{ matrix.os }}, ${{ matrix.vendor }}-${{ matrix.tag }}, ${{ matrix.config }})`, - head_sha: context.sha, + head_sha: headSha, status: 'in_progress' }); return response.data.id; diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index a6228b01de..705158b15a 100755 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -149,6 +149,8 @@ if(CMAKE_TOOLCHAIN_FILE) list(APPEND NBL_JPEG_CMAKE_OPTIONS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") endif() +nbl_append_sanitize_address_cmake_options(NBL_JPEG_CMAKE_OPTIONS) + # TODO: might find an alternative library which supports add_subdirectory, untill then we need to switch to a workaround just like # we do for DXC due to: https://github.com/libjpeg-turbo/libjpeg-turbo/blob/0b742742c873025e2a127918d4969238ace7ae5b/CMakeLists.txt#L69 execute_process(COMMAND "${CMAKE_COMMAND}" -S "${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo" -B "${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo" -G "${CMAKE_GENERATOR}" ${NBL_JPEG_CMAKE_OPTIONS} @@ -492,10 +494,6 @@ endif() foreach(trgt IN LISTS NBL_3RDPARTY_TARGETS) set_property(TARGET ${trgt} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>$<$:DLL>") - if(MSVC AND NBL_SANITIZE_ADDRESS) - set_property(TARGET ${trgt} PROPERTY COMPILE_OPTIONS /fsanitize=address) - endif() - get_target_property(NBL_TARGET_TYPE ${trgt} TYPE) if(NOT "${NBL_TARGET_TYPE}" STREQUAL "INTERFACE_LIBRARY") # maybe explicit global mapping would be better, to discuss diff --git a/3rdparty/dxc/CMakeLists.txt b/3rdparty/dxc/CMakeLists.txt index d6ea0d0554..506dd9cc60 100644 --- a/3rdparty/dxc/CMakeLists.txt +++ b/3rdparty/dxc/CMakeLists.txt @@ -43,6 +43,8 @@ list(APPEND NBL_DXC_CMAKE_OPTIONS "-DDXC_SPIRV_TOOLS_DIR=${DXC_SPIRV_TOOLS_DIR}" list(APPEND NBL_DXC_CMAKE_OPTIONS "-DDXC_SPIRV_HEADERS_DIR=${DXC_SPIRV_HEADERS_DIR}") list(APPEND NBL_DXC_CMAKE_OPTIONS "-DDXC_ENABLE_ETW=OFF") +nbl_append_sanitize_address_cmake_options(NBL_DXC_CMAKE_OPTIONS) + if(NOT NBL_IS_MULTI_CONFIG) list(APPEND NBL_DXC_CMAKE_OPTIONS "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") endif() @@ -54,7 +56,9 @@ endif() #else() # NBL_EXT_P_APPEND_COMPILE_OPTIONS(NBL_DXC_CMAKE_OPTIONS Release RelWithDebInfo Debug) #endif() -#list(TRANSFORM NBL_DXC_CMAKE_OPTIONS REPLACE "/fp:fast" "/fp:precise") +if(MSVC AND NBL_SANITIZE_ADDRESS) + list(TRANSFORM NBL_DXC_CMAKE_OPTIONS REPLACE "/fp:fast" "/fp:precise") +endif() if(WIN32) if(NOT DEFINED HLSL_AUTOCRLF) diff --git a/3rdparty/dxc/dxc b/3rdparty/dxc/dxc index dafad1d9a3..d76c7890b1 160000 --- a/3rdparty/dxc/dxc +++ b/3rdparty/dxc/dxc @@ -1 +1 @@ -Subproject commit dafad1d9a370d17ac9ce69928ef518f842cb5191 +Subproject commit d76c7890b19ce0b344ee0ce116dbc1c92220ccea diff --git a/3rdparty/gli b/3rdparty/gli index c4e6446d3b..2749a197e8 160000 --- a/3rdparty/gli +++ b/3rdparty/gli @@ -1 +1 @@ -Subproject commit c4e6446d3b646538026fd5a95533daed952878d4 +Subproject commit 2749a197e88f94858f4108732824b3790064f6ec diff --git a/3rdparty/glm b/3rdparty/glm index 2d4c4b4dd3..8f6213d379 160000 --- a/3rdparty/glm +++ b/3rdparty/glm @@ -1 +1 @@ -Subproject commit 2d4c4b4dd31fde06cfffad7915c2b3006402322f +Subproject commit 8f6213d379a904f5ae910e09a114e066e25faf57 diff --git a/3rdparty/openexr b/3rdparty/openexr index c8a74d9ac9..aaf5f750d7 160000 --- a/3rdparty/openexr +++ b/3rdparty/openexr @@ -1 +1 @@ -Subproject commit c8a74d9ac97dd579a47a7913f361a87349c0fffd +Subproject commit aaf5f750d7a5fd117d79932d209f0e9816cbff1f diff --git a/CMakeLists.txt b/CMakeLists.txt index bedb9f1dc2..c21da262c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,10 @@ # For conditions of distribution and use, see copyright notice in nabla.h.in or nabla.h cmake_minimum_required(VERSION 3.31..4.2.0) +if(NOT DEFINED CMAKE_POLICY_VERSION_MINIMUM) + set(CMAKE_POLICY_VERSION_MINIMUM 3.31 CACHE STRING "Minimum policy version for third-party projects") +endif() + # TODO: Yas - once we deploy 4.x we will fire `cmake_policy(VERSION [...])` instead of manually picking policies # https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html#policy-version # also we should update deps which throw warnings about < 3.10 compatibility @@ -28,7 +32,7 @@ include(ExternalProject) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/toolchains/android/build.cmake) project(Nabla - VERSION 0.8.0.1 + VERSION 0.9.0.0 HOMEPAGE_URL "https://www.devsh.eu/nabla" LANGUAGES CXX C ) @@ -46,6 +50,7 @@ option(NBL_STATIC_BUILD "" OFF) # ON for static builds, OFF for shared option(NBL_COMPILER_DYNAMIC_RUNTIME "" ON) option(NBL_SANITIZE_ADDRESS OFF) +option(NBL_DEBUG_RTC_ENABLED "Enable Runtime Checks for Debug builds" OFF) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT $<$:ProgramDatabase>) # ignored on non xMSVC-ABI targets @@ -173,8 +178,9 @@ option(NBL_BUILD_DPL "Enable DPL (Dynamic Parallelism Library)" OFF) option(NBL_PCH "Enable pre-compiled header" ON) option(NBL_FAST_MATH "Enable fast low-precision math" OFF) # the reason OFF is by default now is the var controling it at build time was set AFTER BuildConfigOptions was generated - resulting in the feature being always OFF regardless the value xD - so just for sanity, keeping the same behaviour by default 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_MITSUBA_LOADER "Enable nbl::ext::MitsubaLoader?" ON) option(NBL_BUILD_IMGUI "Enable nbl::ext::ImGui?" ON) +option(NBL_BUILD_DEBUG_DRAW "Enable Nabla Debug Draw extension?" ON) option(NBL_BUILD_OPTIX "Enable nbl::ext::OptiX?" OFF) if(NBL_COMPILE_WITH_CUDA) diff --git a/CMakePresets.json b/CMakePresets.json index e91c46d8e9..3117e607ac 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -15,7 +15,7 @@ "NBL_UPDATE_GIT_SUBMODULE": "OFF", "NBL_COMPILE_WITH_CUDA": "OFF", "NBL_BUILD_OPTIX": "OFF", - "NBL_BUILD_MITSUBA_LOADER": "OFF", + "NBL_BUILD_MITSUBA_LOADER": "ON", "NBL_BUILD_RADEON_RAYS": "OFF", "_NBL_COMPILE_WITH_OPEN_EXR_": "ON", "NBL_EXPLICIT_MODULE_LOAD_LOG": "ON", diff --git a/cmake/adjust/flags.cmake b/cmake/adjust/flags.cmake index 1e67914ae0..564f5f7ff7 100644 --- a/cmake/adjust/flags.cmake +++ b/cmake/adjust/flags.cmake @@ -281,6 +281,7 @@ function(nbl_adjust_flags) # global compile options list(APPEND _D_NBL_COMPILE_OPTIONS_ ${NBL_COMPILE_OPTIONS}) + list(APPEND _D_NBL_LINK_OPTIONS_ ${NBL_LINK_OPTIONS}) foreach(CONFIG ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER "${CONFIG}" CONFIG_U) @@ -316,15 +317,21 @@ function(nbl_adjust_flags) # global compile options list(APPEND _D_NBL_COMPILE_OPTIONS_ ${NBL_COMPILE_OPTIONS}) + list(APPEND _D_NBL_LINK_OPTIONS_ ${NBL_LINK_OPTIONS}) foreach(_NBL_OPTION_IMPL_ ${_NBL_OPTIONS_IMPL_}) string(REPLACE "NBL_MAP_" "" NBL_MAP_CONFIGURATION_FROM "NBL_${_NBL_OPTION_IMPL_}") string(TOUPPER "${NBL_${_NBL_OPTION_IMPL_}}" NBL_MAP_CONFIGURATION_TO) set(NBL_TO_CONFIG_COMPILE_OPTIONS ${NBL_${NBL_MAP_CONFIGURATION_TO}_COMPILE_OPTIONS}) + set(NBL_TO_CONFIG_LINK_OPTIONS ${NBL_${NBL_MAP_CONFIGURATION_TO}_LINK_OPTIONS}) # per configuration compile options with mapping list(APPEND _D_NBL_COMPILE_OPTIONS_ $<$:${NBL_TO_CONFIG_COMPILE_OPTIONS}>) + list(APPEND _D_NBL_LINK_OPTIONS_ $<$:${NBL_TO_CONFIG_LINK_OPTIONS}>) endforeach() - set_directory_properties(PROPERTIES COMPILE_OPTIONS "${_D_NBL_COMPILE_OPTIONS_}") + set_directory_properties(PROPERTIES + COMPILE_OPTIONS "${_D_NBL_COMPILE_OPTIONS_}" + LINK_OPTIONS "${_D_NBL_LINK_OPTIONS_}" + ) endif() -endfunction() \ No newline at end of file +endfunction() diff --git a/cmake/adjust/template/vendor/impl/Clang.cmake b/cmake/adjust/template/vendor/impl/Clang.cmake index 0b00294411..984dc94681 100644 --- a/cmake/adjust/template/vendor/impl/Clang.cmake +++ b/cmake/adjust/template/vendor/impl/Clang.cmake @@ -77,11 +77,11 @@ else() endif() if(NBL_SANITIZE_ADDRESS) - NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} COMPILE_OPTIONS -fsanitize=address) + NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} COMPILE_OPTIONS -fsanitize=address LINK_OPTIONS -fsanitize=address) endif() if(NBL_SANITIZE_THREAD) - NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} COMPILE_OPTIONS -fsanitize=thread) + NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} COMPILE_OPTIONS -fsanitize=thread LINK_OPTIONS -fsanitize=thread) endif() NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} CONFIG DEBUG COMPILE_OPTIONS @@ -106,4 +106,4 @@ else() -mno-incremental-linker-compatible # https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mincremental-linker-compatible -DNDEBUG ) -endif() \ No newline at end of file +endif() diff --git a/cmake/adjust/template/vendor/impl/frontend/MSVC.cmake b/cmake/adjust/template/vendor/impl/frontend/MSVC.cmake index 5b5e8c8f50..f433b553e3 100644 --- a/cmake/adjust/template/vendor/impl/frontend/MSVC.cmake +++ b/cmake/adjust/template/vendor/impl/frontend/MSVC.cmake @@ -23,20 +23,24 @@ if(NBL_SANITIZE_ADDRESS) NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} COMPILE_OPTIONS /fsanitize=address # https://learn.microsoft.com/en-us/cpp/build/reference/fsanitize?view=msvc-170 ) - - NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} CONFIG DEBUG COMPILE_OPTIONS - /RTC1 # https://learn.microsoft.com/en-us/cpp/build/reference/rtc-run-time-error-checks?view=msvc-170 - ) endif() -NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} CONFIG DEBUG COMPILE_OPTIONS +set(_NBL_MSVC_DEBUG_COMPILE_OPTIONS /Ob0 # https://learn.microsoft.com/en-us/cpp/build/reference/ob-inline-function-expansion?view=msvc-170 /Od # https://learn.microsoft.com/en-us/cpp/build/reference/od-disable-debug?view=msvc-170 /Oy- # https://learn.microsoft.com/en-us/cpp/build/reference/oy-frame-pointer-omission?view=msvc-170 +) +if(NBL_DEBUG_RTC_ENABLED AND NOT NBL_SANITIZE_ADDRESS) + list(APPEND _NBL_MSVC_DEBUG_COMPILE_OPTIONS /RTC1) +endif() + +NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} CONFIG DEBUG COMPILE_OPTIONS + ${_NBL_MSVC_DEBUG_COMPILE_OPTIONS} LINK_OPTIONS /INCREMENTAL # https://learn.microsoft.com/en-us/cpp/build/reference/incremental-link-incrementally?view=msvc-170 ) +unset(_NBL_MSVC_DEBUG_COMPILE_OPTIONS) NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} CONFIG RELEASE COMPILE_OPTIONS /O2 # https://learn.microsoft.com/en-us/cpp/build/reference/o1-o2-minimize-size-maximize-speed?view=msvc-170 @@ -63,4 +67,4 @@ NBL_REQUEST_COMPILE_OPTION_SUPPORT(LANG ${LANG} CONFIG RELWITHDEBINFO COMPILE_OP LINK_OPTIONS /INCREMENTAL # https://learn.microsoft.com/en-us/cpp/build/reference/incremental-link-incrementally?view=msvc-170 -) \ No newline at end of file +) diff --git a/cmake/common.cmake b/cmake/common.cmake index 645837aaaa..6a3b5ec990 100755 --- a/cmake/common.cmake +++ b/cmake/common.cmake @@ -16,6 +16,34 @@ include_guard(GLOBAL) include(ProcessorCount) +# tmp for external projects, to be removed later when I get rid of them (dxc + jpeg currently) +function(nbl_append_sanitize_address_cmake_options out_list) + if(NOT NBL_SANITIZE_ADDRESS) + return() + endif() + + if(MSVC) + set(_NBL_ASAN_FLAG "/fsanitize=address") + else() + set(_NBL_ASAN_FLAG "-fsanitize=address") + set(_NBL_ASAN_LINK_SUFFIX " ${_NBL_ASAN_FLAG}") + endif() + + list(APPEND ${out_list} + "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} ${_NBL_ASAN_FLAG}" + "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${_NBL_ASAN_FLAG}" + ) + if(DEFINED _NBL_ASAN_LINK_SUFFIX) + list(APPEND ${out_list} + "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}${_NBL_ASAN_LINK_SUFFIX}" + "-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS}${_NBL_ASAN_LINK_SUFFIX}" + ) + endif() + unset(_NBL_ASAN_FLAG) + unset(_NBL_ASAN_LINK_SUFFIX) + set(${out_list} "${${out_list}}" PARENT_SCOPE) +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 @@ -350,13 +378,6 @@ function(nbl_install_media _FILE) nbl_install_lib_spec("${_FILE}" "") endfunction() -function(nbl_install_builtin_resources _TARGET_) - get_target_property(_BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY_ ${_TARGET_} BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY) - get_target_property(_BUILTIN_RESOURCES_HEADERS_ ${_TARGET_} BUILTIN_RESOURCES_HEADERS) - - nbl_install_headers_spec("${_BUILTIN_RESOURCES_HEADERS_}" "${_BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY_}") -endfunction() - function(NBL_TEST_MODULE_INSTALL_FILE _NBL_FILEPATH_) file(RELATIVE_PATH _NBL_REL_INSTALL_DEST_ "${NBL_ROOT_PATH}" "${_NBL_FILEPATH_}") cmake_path(GET _NBL_REL_INSTALL_DEST_ PARENT_PATH _NBL_REL_INSTALL_DEST_) @@ -1144,6 +1165,13 @@ define_property(TARGET PROPERTY NBL_MOUNT_POINT_DEFINES BRIEF_DOCS "List of preprocessor defines with mount points" ) +option(NSC_DEBUG_EDIF_FILE_BIT "Add \"-fspv-debug=file\" to NSC Debug CLI" ON) +option(NSC_DEBUG_EDIF_SOURCE_BIT "Add \"-fspv-debug=source\" to NSC Debug CLI" OFF) +option(NSC_DEBUG_EDIF_LINE_BIT "Add \"-fspv-debug=line\" to NSC Debug CLI" OFF) +option(NSC_DEBUG_EDIF_TOOL_BIT "Add \"-fspv-debug=tool\" to NSC Debug CLI" ON) +option(NSC_DEBUG_EDIF_NON_SEMANTIC_BIT "Add \"-fspv-debug=vulkan-with-source\" to NSC Debug CLI" OFF) +option(NSC_USE_DEPFILE "Generate depfiles for NSC custom commands" ON) + function(NBL_CREATE_NSC_COMPILE_RULES) set(COMMENT "this code has been autogenerated with Nabla CMake NBL_CREATE_HLSL_COMPILE_RULES utility") set(DEVICE_CONFIG_VIEW @@ -1178,11 +1206,39 @@ struct DeviceConfigCaps -enable-16bit-types -Zpr -spirv - -fspv-target-env=vulkan1.3 + -Wno-local-type-template-args + -fspv-target-env=vulkan1.3 + -Wshadow + -Wconversion + -Wno-local-type-template-args + $<$:-O0> + $<$:-O3> + $<$:-O3> ) + if(NSC_DEBUG_EDIF_FILE_BIT) + list(APPEND REQUIRED_OPTIONS $<$:-fspv-debug=file>) + endif() + + if(NSC_DEBUG_EDIF_SOURCE_BIT) + list(APPEND REQUIRED_OPTIONS $<$:-fspv-debug=source>) + endif() + + if(NSC_DEBUG_EDIF_LINE_BIT) + list(APPEND REQUIRED_OPTIONS $<$:-fspv-debug=line>) + endif() + + if(NSC_DEBUG_EDIF_TOOL_BIT) + list(APPEND REQUIRED_OPTIONS $<$:-fspv-debug=tool>) + endif() + + if(NSC_DEBUG_EDIF_NON_SEMANTIC_BIT) + list(APPEND REQUIRED_OPTIONS $<$:-fspv-debug=vulkan-with-source>) + endif() + if(NOT NBL_EMBED_BUILTIN_RESOURCES) list(APPEND REQUIRED_OPTIONS + -no-nbl-builtins -I "${NBL_ROOT_PATH}/include" -I "${NBL_ROOT_PATH}/3rdparty/dxc/dxc/external/SPIRV-Headers/include" -I "${NBL_ROOT_PATH}/3rdparty/boost/superproject/libs/preprocessor/include" @@ -1191,9 +1247,20 @@ struct DeviceConfigCaps endif() set(REQUIRED_SINGLE_ARGS TARGET BINARY_DIR OUTPUT_VAR INPUTS INCLUDE NAMESPACE MOUNT_POINT_DEFINE) - cmake_parse_arguments(IMPL "" "${REQUIRED_SINGLE_ARGS};LINK_TO" "COMMON_OPTIONS;DEPENDS" ${ARGV}) + set(OPTIONAL_SINGLE_ARGS GLOB_DIR) + cmake_parse_arguments(IMPL "DISCARD_DEFAULT_GLOB" "${REQUIRED_SINGLE_ARGS};${OPTIONAL_SINGLE_ARGS};LINK_TO" "COMMON_OPTIONS;DEPENDS" ${ARGV}) NBL_PARSE_REQUIRED(IMPL ${REQUIRED_SINGLE_ARGS}) + set(IMPL_HLSL_GLOB "") + if(NOT IMPL_DISCARD_DEFAULT_GLOB) + set(GLOB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}") + if(IMPL_GLOB_DIR) + set(GLOB_ROOT "${IMPL_GLOB_DIR}") + endif() + get_filename_component(GLOB_ROOT "${GLOB_ROOT}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + file(GLOB_RECURSE IMPL_HLSL_GLOB CONFIGURE_DEPENDS "${GLOB_ROOT}/*.hlsl") + endif() + if(NOT TARGET ${IMPL_TARGET}) add_library(${IMPL_TARGET} INTERFACE) endif() @@ -1210,12 +1277,12 @@ struct DeviceConfigCaps get_target_property(HEADER_RULE_GENERATED ${IMPL_TARGET} NBL_HEADER_GENERATED_RULE) if(NOT HEADER_RULE_GENERATED) - set(INCLUDE_DIR "$/${IMPL_TARGET}/.cmake/include") + set(INCLUDE_DIR "$/${IMPL_TARGET}/.cmake/include/$") set(INCLUDE_FILE "${INCLUDE_DIR}/$") set(INCLUDE_CONTENT $) file(GENERATE OUTPUT ${INCLUDE_FILE} - CONTENT ${INCLUDE_CONTENT} + CONTENT $ TARGET ${IMPL_TARGET} ) @@ -1269,6 +1336,10 @@ namespace @IMPL_NAMESPACE@ { list(APPEND MP_DEFINES ${IMPL_MOUNT_POINT_DEFINE}="${IMPL_BINARY_DIR}") set_target_properties(${IMPL_TARGET} PROPERTIES NBL_MOUNT_POINT_DEFINES "${MP_DEFINES}") + set(RTE "NSC Rules") + set(IN "${RTE}/In") + set(OUT "${RTE}/Out") + string(JSON JSON_LENGTH LENGTH "${IMPL_INPUTS}") math(EXPR LAST_INDEX "${JSON_LENGTH} - 1") @@ -1277,17 +1348,22 @@ namespace @IMPL_NAMESPACE@ { foreach(INDEX RANGE ${LAST_INDEX}) string(JSON INPUT GET "${IMPL_INPUTS}" ${INDEX} INPUT) string(JSON BASE_KEY GET "${IMPL_INPUTS}" ${INDEX} KEY) - string(JSON COMPILE_OPTIONS_LENGTH LENGTH "${IMPL_INPUTS}" ${INDEX} COMPILE_OPTIONS) - + set(COMPILE_OPTIONS "") - math(EXPR LAST_CO "${COMPILE_OPTIONS_LENGTH} - 1") - foreach(COMP_IDX RANGE 0 ${LAST_CO}) - string(JSON COMP_ITEM GET "${IMPL_INPUTS}" ${INDEX} COMPILE_OPTIONS ${COMP_IDX}) - list(APPEND COMPILE_OPTIONS "${COMP_ITEM}") - endforeach() + string(JSON HAS_COMPILE_OPTIONS ERROR_VARIABLE ERROR_VAR TYPE "${IMPL_INPUTS}" ${INDEX} COMPILE_OPTIONS) + if(HAS_COMPILE_OPTIONS STREQUAL "ARRAY") + string(JSON COMPILE_OPTIONS_LENGTH LENGTH "${IMPL_INPUTS}" ${INDEX} COMPILE_OPTIONS) + if(NOT COMPILE_OPTIONS_LENGTH EQUAL 0) + math(EXPR LAST_CO "${COMPILE_OPTIONS_LENGTH} - 1") + foreach(COMP_IDX RANGE 0 ${LAST_CO}) + string(JSON COMP_ITEM GET "${IMPL_INPUTS}" ${INDEX} COMPILE_OPTIONS ${COMP_IDX}) + list(APPEND COMPILE_OPTIONS "${COMP_ITEM}") + endforeach() + endif() + endif() set(DEPENDS_ON "") - string(JSON HAS_DEPENDS TYPE "${IMPL_INPUTS}" ${INDEX} DEPENDS) + string(JSON HAS_DEPENDS ERROR_VARIABLE ERROR_VAR TYPE "${IMPL_INPUTS}" ${INDEX} DEPENDS) if(HAS_DEPENDS STREQUAL "ARRAY") string(JSON DEPENDS_LENGTH LENGTH "${IMPL_INPUTS}" ${INDEX} DEPENDS) if(NOT DEPENDS_LENGTH EQUAL 0) @@ -1305,7 +1381,7 @@ namespace @IMPL_NAMESPACE@ { set(HAS_CAPS FALSE) set(CAPS_LENGTH 0) - string(JSON CAPS_TYPE TYPE "${IMPL_INPUTS}" ${INDEX} CAPS) + string(JSON CAPS_TYPE ERROR_VARIABLE ERROR_VAR TYPE "${IMPL_INPUTS}" ${INDEX} CAPS) if(CAPS_TYPE STREQUAL "ARRAY") string(JSON CAPS_LENGTH LENGTH "${IMPL_INPUTS}" ${INDEX} CAPS) if(NOT CAPS_LENGTH EQUAL 0) @@ -1323,12 +1399,27 @@ namespace @IMPL_NAMESPACE@ { set(CAP_NAMES "") set(CAP_TYPES "") + set(CAP_KINDS "") if(HAS_CAPS) math(EXPR LAST_CAP "${CAPS_LENGTH} - 1") foreach(CAP_IDX RANGE 0 ${LAST_CAP}) + string(JSON CAP_KIND ERROR_VARIABLE CAP_TYPE_ERROR GET "${IMPL_INPUTS}" ${INDEX} CAPS ${CAP_IDX} kind) string(JSON CAP_NAME GET "${IMPL_INPUTS}" ${INDEX} CAPS ${CAP_IDX} name) string(JSON CAP_TYPE GET "${IMPL_INPUTS}" ${INDEX} CAPS ${CAP_IDX} type) + # -> TODO: improve validation, input should be string + if(CAP_TYPE_ERROR) + set(CAP_KIND limits) # I assume its limit by default (or when invalid value present, currently) + else() + if(NOT CAP_KIND MATCHES "^(limits|features)$") + ERROR_WHILE_PARSING_ITEM( + "Invalid CAP kind \"${CAP_KIND}\" for ${CAP_NAME}\n" + "Allowed kinds are: limits, features" + ) + endif() + endif() + # <- + if(NOT CAP_TYPE MATCHES "^(bool|uint16_t|uint32_t|uint64_t)$") ERROR_WHILE_PARSING_ITEM( "Invalid CAP type \"${CAP_TYPE}\" for ${CAP_NAME}\n" @@ -1366,6 +1457,7 @@ namespace @IMPL_NAMESPACE@ { set(CAP_VALUES_${CAP_IDX} "${VALUES}") list(APPEND CAP_NAMES "${CAP_NAME}") list(APPEND CAP_TYPES "${CAP_TYPE}") + list(APPEND CAP_KINDS "${CAP_KIND}") endforeach() endif() @@ -1399,68 +1491,122 @@ namespace @IMPL_NAMESPACE@ { nbl::core::string retval = "@BASE_KEY@"; @RETVAL_EVAL@ retval += ".spv"; - return retval; + return "$/" + retval; } } ]=]) unset(RETVAL_EVAL) - foreach(CAP ${CAP_NAMES}) - string(CONFIGURE [=[ - retval += ".@CAP@_" + std::to_string(limits.@CAP@); -]=] RETVALUE_VIEW @ONLY) - string(APPEND RETVAL_EVAL "${RETVALUE_VIEW}") - endforeach(CAP) + list(LENGTH CAP_NAMES CAP_COUNT) + if(CAP_COUNT GREATER 0) + math(EXPR LAST_CAP "${CAP_COUNT} - 1") + foreach(i RANGE ${LAST_CAP}) + list(GET CAP_NAMES ${i} CAP) + list(GET CAP_KINDS ${i} KIND) + string(CONFIGURE [=[ + retval += ".@CAP@_" + std::to_string(@KIND@.@CAP@); +]=] RETVALUE_VIEW @ONLY) + string(APPEND RETVAL_EVAL "${RETVALUE_VIEW}") + endforeach() + endif() + string(CONFIGURE "${HEADER_ITEM_VIEW}" HEADER_ITEM_EVAL @ONLY) set_property(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY NBL_HEADER_CONTENT "${HEADER_ITEM_EVAL}") function(GENERATE_KEYS PREFIX CAP_INDEX CAPS_EVAL_PART) if(NUM_CAPS EQUAL 0 OR CAP_INDEX EQUAL ${NUM_CAPS}) + # generate .config file set(FINAL_KEY "${BASE_KEY}${PREFIX}.spv") # always add ext even if its already there to make sure asset loader always is able to load as IShader - - set(TARGET_OUTPUT "${IMPL_BINARY_DIR}/${FINAL_KEY}") - set(CONFIG_FILE "${TARGET_OUTPUT}.config") + set(CONFIG_FILE_TARGET_OUTPUT "${IMPL_BINARY_DIR}/${FINAL_KEY}") + set(CONFIG_FILE "${CONFIG_FILE_TARGET_OUTPUT}.config") set(CAPS_EVAL "${CAPS_EVAL_PART}") - string(CONFIGURE "${DEVICE_CONFIG_VIEW}" CONFIG_CONTENT @ONLY) file(WRITE "${CONFIG_FILE}" "${CONFIG_CONTENT}") + # generate keys and commands for compiling shaders + set(FINAL_KEY_REL_PATH "$/${FINAL_KEY}") + set(TARGET_OUTPUT "${IMPL_BINARY_DIR}/${FINAL_KEY_REL_PATH}") + set(DEPFILE_PATH "${TARGET_OUTPUT}.d") + set(NBL_NSC_LOG_PATH "${TARGET_OUTPUT}.log") + + set(NBL_NSC_DEPFILE_ARGS "") + if(NSC_USE_DEPFILE) + set(NBL_NSC_DEPFILE_ARGS -MD -MF "${DEPFILE_PATH}") + endif() + set(NBL_NSC_COMPILE_COMMAND "$" -Fc "${TARGET_OUTPUT}" ${COMPILE_OPTIONS} ${REQUIRED_OPTIONS} ${IMPL_COMMON_OPTIONS} + ${NBL_NSC_DEPFILE_ARGS} "${CONFIG_FILE}" ) - add_custom_command(OUTPUT "${TARGET_OUTPUT}" + get_filename_component(NBL_NSC_INPUT_NAME "${TARGET_INPUT}" NAME) + get_filename_component(NBL_NSC_CONFIG_NAME "${CONFIG_FILE}" NAME) + set(NBL_NSC_BYPRODUCTS "${NBL_NSC_LOG_PATH}") + if(NSC_USE_DEPFILE) + list(APPEND NBL_NSC_BYPRODUCTS "${DEPFILE_PATH}") + endif() + + set(NBL_NSC_CUSTOM_COMMAND_ARGS + OUTPUT "${TARGET_OUTPUT}" + BYPRODUCTS ${NBL_NSC_BYPRODUCTS} COMMAND ${NBL_NSC_COMPILE_COMMAND} DEPENDS ${DEPENDS_ON} - COMMENT "Creating \"${TARGET_OUTPUT}\"" + COMMENT "${NBL_NSC_CONFIG_NAME} (${NBL_NSC_INPUT_NAME})" VERBATIM COMMAND_EXPAND_LISTS ) + if(NSC_USE_DEPFILE) + list(APPEND NBL_NSC_CUSTOM_COMMAND_ARGS DEPFILE "${DEPFILE_PATH}") + endif() + add_custom_command(${NBL_NSC_CUSTOM_COMMAND_ARGS}) + set(NBL_NSC_OUT_FILES "${TARGET_OUTPUT}" "${NBL_NSC_LOG_PATH}") + if(NSC_USE_DEPFILE) + list(APPEND NBL_NSC_OUT_FILES "${DEPFILE_PATH}") + endif() + + set_source_files_properties(${NBL_NSC_OUT_FILES} PROPERTIES GENERATED TRUE) - set(HEADER_ONLY_LIKE "${CONFIG_FILE}" "${TARGET_INPUT}" "${TARGET_OUTPUT}") + set(HEADER_ONLY_LIKE "${CONFIG_FILE}" "${TARGET_INPUT}" ${NBL_NSC_OUT_FILES}) target_sources(${IMPL_TARGET} PRIVATE ${HEADER_ONLY_LIKE}) set_source_files_properties(${HEADER_ONLY_LIKE} PROPERTIES HEADER_FILE_ONLY ON VS_TOOL_OVERRIDE None ) + if(CMAKE_CONFIGURATION_TYPES) + foreach(_CFG IN LISTS CMAKE_CONFIGURATION_TYPES) + set(TARGET_OUTPUT_IDE "${IMPL_BINARY_DIR}/${_CFG}/${FINAL_KEY}") + set(NBL_NSC_OUT_FILES_IDE "${TARGET_OUTPUT_IDE}" "${TARGET_OUTPUT_IDE}.log") + if(NSC_USE_DEPFILE) + list(APPEND NBL_NSC_OUT_FILES_IDE "${TARGET_OUTPUT_IDE}.d") + endif() + source_group("${OUT}/${_CFG}" FILES ${NBL_NSC_OUT_FILES_IDE}) + endforeach() + else() + set(TARGET_OUTPUT_IDE "${IMPL_BINARY_DIR}/${FINAL_KEY}") + set(NBL_NSC_OUT_FILES_IDE "${TARGET_OUTPUT_IDE}" "${TARGET_OUTPUT_IDE}.log") + if(NSC_USE_DEPFILE) + list(APPEND NBL_NSC_OUT_FILES_IDE "${TARGET_OUTPUT_IDE}.d") + endif() + source_group("${OUT}" FILES ${NBL_NSC_OUT_FILES_IDE}) + endif() set_source_files_properties("${TARGET_OUTPUT}" PROPERTIES NBL_SPIRV_REGISTERED_INPUT "${TARGET_INPUT}" NBL_SPIRV_PERMUTATION_CONFIG "${CONFIG_FILE}" NBL_SPIRV_BINARY_DIR "${IMPL_BINARY_DIR}" - NBL_SPIRV_ACCESS_KEY "${FINAL_KEY}" + NBL_SPIRV_ACCESS_KEY "${FINAL_KEY_REL_PATH}" ) - set_property(TARGET ${IMPL_TARGET} APPEND PROPERTY NBL_SPIRV_OUTPUTS "${TARGET_OUTPUT}") return() endif() list(GET CAP_NAMES ${CAP_INDEX} CURRENT_CAP) list(GET CAP_TYPES ${CAP_INDEX} CURRENT_TYPE) + list(GET CAP_KINDS ${CAP_INDEX} CURRENT_KIND) set(VAR_NAME "CAP_VALUES_${CAP_INDEX}") set(VALUES "${${VAR_NAME}}") @@ -1487,12 +1633,15 @@ namespace @IMPL_NAMESPACE@ { list(APPEND KEYS ${ACCESS_KEY}) endforeach() - set(RTE "NSC Rules") - set(IN "${RTE}/In") - set(OUT "${RTE}/Out") - source_group("${IN}" FILES ${CONFIGS} ${INPUTS}) - source_group("${OUT}" FILES ${SPIRVs}) + if(IMPL_HLSL_GLOB) + target_sources(${IMPL_TARGET} PRIVATE ${IMPL_HLSL_GLOB}) + set_source_files_properties(${IMPL_HLSL_GLOB} PROPERTIES + HEADER_FILE_ONLY ON + VS_TOOL_OVERRIDE None + ) + source_group("HLSL Files" FILES ${IMPL_HLSL_GLOB}) + endif() set(${IMPL_OUTPUT_VAR} ${KEYS} PARENT_SCOPE) endfunction() @@ -1534,4 +1683,4 @@ function(NBL_CREATE_RESOURCE_ARCHIVE) if(IMPL_LINK_TO) LINK_BUILTIN_RESOURCES_TO_TARGET(${IMPL_LINK_TO} ${IMPL_TARGET}) endif() -endfunction() \ No newline at end of file +endfunction() diff --git a/docker/compiler-explorer b/docker/compiler-explorer index 45866dfa87..27318d12f8 160000 --- a/docker/compiler-explorer +++ b/docker/compiler-explorer @@ -1 +1 @@ -Subproject commit 45866dfa8782404fc121f25ce15ad0626b474db0 +Subproject commit 27318d12f88cf34bd0444101e6e260b12f5063a0 diff --git a/docs/nsc-prebuilds.md b/docs/nsc-prebuilds.md new file mode 100644 index 0000000000..400aff5eb7 --- /dev/null +++ b/docs/nsc-prebuilds.md @@ -0,0 +1,369 @@ +# NSC prebuilds (build-time HLSL -> SPIR-V) + +This document explains how to use `NBL_CREATE_NSC_COMPILE_RULES` together with `NBL_CREATE_RESOURCE_ARCHIVE` to: + +- Compile HLSL to SPIR-V at **build time** (via the `nsc` tool). +- Optionally generate **device-cap permutations** (limits/features "CAPS"). +- Generate a small C++ header with **type-safe key getters** (`get_spirv_key<...>()`). +- Make the same code work with `NBL_EMBED_BUILTIN_RESOURCES` **ON** (embedded virtual archive) and **OFF** (mounted build directory) when loading your precompiled SPIR-V at runtime. + +Definitions live in `cmake/common.cmake` (`NBL_CREATE_NSC_COMPILE_RULES`, `NBL_CREATE_RESOURCE_ARCHIVE`). + +## Runtime mounting requirement (important) + +All of this assumes your app mounts the directory/archive containing the NSC outputs (i.e. `BINARY_DIR`) into Nabla's virtual filesystem, then loads files via keys that are relative to that mounted root (the examples use `app_resources`). + +The examples "just work" because they inherit from `nbl::examples::BuiltinResourcesApplication`, which mounts: + +- `NBL_EMBED_BUILTIN_RESOURCES=OFF`: `system::CMountDirectoryArchive(NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT, ...)` at `app_resources` +- `NBL_EMBED_BUILTIN_RESOURCES=ON`: the generated embedded archive (e.g. `nbl::this_example::builtin::build::CArchive`) at `app_resources` + +If you're writing your own app/extension and don't use `BuiltinResourcesApplication`, you must mount equivalently yourself (split by `NBL_EMBED_BUILTIN_RESOURCES`). Optionally set `IAssetLoader::SAssetLoadParams::workingDirectory` to whatever virtual root you want to load from. + +The `MOUNT_POINT_DEFINE` argument of `NBL_CREATE_NSC_COMPILE_RULES` defines a C/C++ macro whose value is the absolute path to the NSC output directory (`BINARY_DIR`) that you mount when builtins are off (in examples it's `NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT`). + +See `examples_tests/common/include/nbl/examples/common/BuiltinResourcesApplication.hpp` for the exact mounting logic. + +## Why build-time NSC instead of runtime compilation? + +Build-time compilation is usually preferable because it: + +- Uses your build system's parallelism (Ninja/MSBuild jobs) to compile shaders quickly. +- Writes **only into the build tree** (no source tree pollution, easy clean/reconfigure). +- Lets CI validate "shaders compile" as part of a normal build. +- Enables fast runtime iteration: at runtime you only **pick** the right SPIR-V, you don't compile it. +- Makes shader compilation deterministic and reproducible (toolchain + flags captured by the build). + +Runtime compilation is still useful for prototyping, but (assuming you don't use a runtime shader cache) it can make startup slower and shift failures to runtime instead of CI/build (a cache can hide the repeated cost on subsequent runs; our current one has some rough edges: it writes into the source tree and has issues when compiling many inputs from the same source directory). + +## What `NBL_CREATE_NSC_COMPILE_RULES` produces + +For each registered input it generates: + +- One `.spv` output **per CMake configuration** (`Debug/`, `Release/`, `RelWithDebInfo/`). +- If you use `CAPS`, it generates a **cartesian product** of permutations and emits a `.spv` for each. +- A generated header (you choose the path via `INCLUDE`) containing: + - a primary template `get_spirv_key(limits, features)` and `get_spirv_key(device)` + - explicit specializations for each registered base `KEY` + - the returned key already includes the build config prefix (compiled into the header). + +Keys are strings that match the output layout: + +``` +/(._)(._)....spv +``` + +## The JSON "INPUTS" format + +`INPUTS` is a JSON array of objects. Each object supports: + +- `INPUT` (string, required): path to `.hlsl` (relative to `CMAKE_CURRENT_SOURCE_DIR` or absolute). +- `KEY` (string, required): base key (prefer without `.spv`; it is always appended, so using `foo.spv` will result in `foo.spv.spv`). +- `COMPILE_OPTIONS` (array of strings, optional): per-input extra options (e.g. `["-T","cs_6_8"]`). +- `DEPENDS` (array of strings, optional): extra per-input dependencies that are not discovered via `#include` (see below). +- `CAPS` (array, optional): permutation caps (see below). + +You can register many rules in a single call, and you can call the function multiple times to append rules to the same `TARGET`. + +## Compile options (generator expressions, defaults, debug info) + +`NBL_CREATE_NSC_COMPILE_RULES` combines options from multiple sources: + +- Built-in defaults from the helper (see `cmake/common.cmake`): HLSL version, Vulkan SPIR-V target env, scalar layout, warnings, and per-config optimization flags (e.g. `-O0` for Debug, `-O3` for Release) implemented via CMake generator expressions. +- Global extra options via `COMMON_OPTIONS` (CMake list). +- Per-input extra options via JSON `COMPILE_OPTIONS` (array of strings). + +Both `COMMON_OPTIONS` and JSON `COMPILE_OPTIONS` support CMake generator expressions like `$<$:...>` (the helper uses them itself), so you can make flags configuration-dependent when needed. + +### Debug info for RenderDoc + +The helper also exposes CMake options that append NSC debug flags **only for Debug config** (via generator expressions). Enable them if you want RenderDoc to show source/line information instead of just raw disassembly: + +- `NSC_DEBUG_EDIF_FILE_BIT` (default `ON`) -> `-fspv-debug=file` +- `NSC_DEBUG_EDIF_TOOL_BIT` (default `ON`) -> `-fspv-debug=tool` +- `NSC_DEBUG_EDIF_SOURCE_BIT` (default `OFF`) -> `-fspv-debug=source` +- `NSC_DEBUG_EDIF_LINE_BIT` (default `OFF`) -> `-fspv-debug=line` +- `NSC_DEBUG_EDIF_NON_SEMANTIC_BIT` (default `OFF`) -> `-fspv-debug=vulkan-with-source` + +## Source files and rebuild dependencies (important) + +NSC supports depfiles and the CMake custom commands consume them, so **changes in any `#include`d HLSL file automatically trigger recompilation of the affected `.spv` outputs**. In most cases you no longer need to list includes manually. + +Use `DEPENDS` only for **extra** inputs that are not discovered via `#include` (e.g. a generated header that is not included, a config file read by a custom include generator, or any non-HLSL file that should trigger a rebuild). You can register those extra dependencies if you need them, but in most projects `DEPENDS` should stay empty. + +By default `NBL_CREATE_NSC_COMPILE_RULES` also collects `*.hlsl` files for IDE visibility. It recursively scans the current source directory (or `GLOB_DIR` if provided), adds those files as header-only, and groups them under `HLSL Files`. If you do not want this behavior, pass `DISCARD_DEFAULT_GLOB`. + +- `GLOB_DIR` (optional): root directory for the default `*.hlsl` scan. +- `DISCARD_DEFAULT_GLOB` (flag): disables the default scan and IDE grouping. + +## Minimal usage (no permutations) + +Example pattern (as in `examples_tests/27_MPMCScheduler/CMakeLists.txt`): + +```cmake +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") + +set(JSON [=[ +[ + { + "INPUT": "app_resources/shader.comp.hlsl", + "KEY": "shader", + "COMPILE_OPTIONS": ["-T", "cs_6_8"], + "DEPENDS": [], + "CAPS": [] + } +] +]=]) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS -I ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) +``` + +Then include the generated header and use the key to load the SPIR-V: + +```cpp +#include "nbl/this_example/builtin/build/spirv/keys.hpp" +// ... +auto key = nbl::this_example::builtin::build::get_spirv_key<"shader">(device); +auto bundle = assetMgr->getAsset(key.c_str(), loadParams); +``` + +`OUTPUT_VAR` (here: `KEYS`) is assigned the list of **all** produced access keys (all configurations + all permutations). This list is intended to be fed into `NBL_CREATE_RESOURCE_ARCHIVE(BUILTINS ${KEYS})`. + +## Permutations via `CAPS` + +`CAPS` lets you prebuild multiple SPIR-V variants parameterized by device limits or features. + +Each `CAPS` entry looks like: + +- `kind` (string, optional): `"limits"` or `"features"` (defaults to `"limits"` if omitted/invalid). +- `name` (string, required): identifier used in both generated HLSL config and C++ key (must be a valid C/C++ identifier). +- `type` (string, required): `bool`, `uint16_t`, `uint32_t`, `uint64_t`. +- `values` (array of numbers, required): the values you want to prebuild. + - for `bool`, values must be `0` or `1`. + +At build time, NSC compiles each combination of values (cartesian product). At runtime, `get_spirv_key` appends suffixes using the `limits`/`features` you pass in. + +### Example: mixing `limits` and `features` + +This example permutes over one device limit and one device feature (order matters: the suffix order matches the `CAPS` array order): + +```cmake +set(JSON [=[ +[ + { + "INPUT": "app_resources/shader.hlsl", + "KEY": "shader", + "COMPILE_OPTIONS": ["-T", "lib_6_8"], + "DEPENDS": ["app_resources/common.hlsl"], + "CAPS": [ + { + "kind": "limits", + "name": "maxComputeSharedMemorySize", + "type": "uint32_t", + "values": [16384, 32768, 65536] + }, + { + "kind": "features", + "name": "shaderFloat64", + "type": "bool", + "values": [0, 1] + } + ] + } +] +]=]) + +NBL_CREATE_NSC_COMPILE_RULES( + # ... + OUTPUT_VAR KEYS + INPUTS ${JSON} +) +``` + +This produces `3 * 2 = 6` permutations per build configuration, and `KEYS` contains all of them (for example): + +``` +Debug/shader.maxComputeSharedMemorySize_16384.shaderFloat64_0.spv +Debug/shader.maxComputeSharedMemorySize_16384.shaderFloat64_1.spv +... +``` + +Practical tip: for numeric limits you often want to "bucket" real device values into one of the prebuilt values. The CountingSort example does exactly that: + +- CMake definition: `examples_tests/10_CountingSort/CMakeLists.txt` +- Runtime bucketing: `examples_tests/10_CountingSort/main.cpp` + +```cpp +auto limits = m_physicalDevice->getLimits(); +constexpr std::array AllowedMaxComputeSharedMemorySizes = { 16384, 32768, 65536 }; + +auto upperBoundSharedMemSize = std::upper_bound( + AllowedMaxComputeSharedMemorySizes.begin(), AllowedMaxComputeSharedMemorySizes.end(), limits.maxComputeSharedMemorySize +); +// devices which support less than 16KB of max compute shared memory size are not supported +if (upperBoundSharedMemSize == AllowedMaxComputeSharedMemorySizes.begin()) +{ + m_logger->log("maxComputeSharedMemorySize is too low (%u)", ILogger::E_LOG_LEVEL::ELL_ERROR, limits.maxComputeSharedMemorySize); + exit(0); +} + +limits.maxComputeSharedMemorySize = *(upperBoundSharedMemSize - 1); + +auto key = nbl::this_example::builtin::build::get_spirv_key<"prefix_sum_shader">(limits, m_physicalDevice->getFeatures()); +``` + +## Pairing with `NBL_CREATE_RESOURCE_ARCHIVE` (works with builtins ON/OFF) + +The recommended pattern is to always call `NBL_CREATE_RESOURCE_ARCHIVE` right after the NSC rules, using the produced `KEYS` list: + +```cmake +NBL_CREATE_RESOURCE_ARCHIVE( + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} + NAMESPACE nbl::this_example::builtin::build +) +``` + +### How `BINARY_DIR`, `MOUNT_POINT_DEFINE`, and `BIND` fit together + +- In `NBL_CREATE_NSC_COMPILE_RULES`, `BINARY_DIR` is the output directory where NSC writes the compiled files: + - `${BINARY_DIR}//....spv` +- In `NBL_CREATE_NSC_COMPILE_RULES`, `MOUNT_POINT_DEFINE` is the *name* of a C/C++ preprocessor define whose value is set to the **absolute path** of `BINARY_DIR`. + - Example: `MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT` results in something like `-DNBL_THIS_EXAMPLE_BUILD_MOUNT_POINT="C:/.../auto-gen"` on the target. + - Keys returned by `get_spirv_key<...>()` are relative to that directory; the full path on disk is: + - `${NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT}/` +- In `NBL_CREATE_RESOURCE_ARCHIVE`, `BIND` should point at the same directory as `BINARY_DIR`. + - The `BUILTINS` list entries must be relative to `BIND`. + - This is why pairing it with `OUTPUT_VAR KEYS` works: `KEYS` is exactly the list of relative paths under `BINARY_DIR` that were generated by the NSC rules, so the archive generator knows what to serialize/embed. + +This is designed to work in both modes: + +- `NBL_EMBED_BUILTIN_RESOURCES=OFF`: + - `NBL_CREATE_RESOURCE_ARCHIVE` becomes a no-op (creates a dummy interface target). + - You load SPIR-V from the **build directory** mounted into the virtual filesystem. + - `MOUNT_POINT_DEFINE` provides an absolute path (e.g. `NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT`) for mounting. +- `NBL_EMBED_BUILTIN_RESOURCES=ON`: + - `NBL_CREATE_RESOURCE_ARCHIVE` generates a small library that embeds the listed files into a virtual archive and emits `.../CArchive.h` under the requested `NAMESPACE`. + - You mount the embedded archive instead of a directory; runtime loading code stays the same (keys don't change). + +## Notes / gotchas + +- `INCLUDE` must be a **relative** path (it is emitted under the build tree and added to include dirs automatically). +- Prefer not to include `.spv` in `KEY` (the extension is appended unconditionally); if you do, you'll just get `.spv.spv` in the final filename/key (not an error, just not what you want). +- You can mix: + - per-input `COMPILE_OPTIONS` (inside JSON), and + - global `COMMON_OPTIONS` (CMake list after `COMMON_OPTIONS`). + +## Troubleshooting (no logs / silent NSC failures) + +Sometimes an NSC compile rule fails during the build, but the build output doesn't show a useful log. In that case, run the failing command under a debugger: + +1. Open the generated Visual Studio solution and set the `nsc` project/target as the Startup Project. +2. Open the `nsc` project properties and set **Debugging -> Command Arguments**. +3. Copy the exact CLI from the failing "NSC Rules" custom command (the one that calls `nsc.exe`) into the Command Arguments field. +4. Start debugging (`F5`) and reproduce; if needed, put a breakpoint in the HLSL compiler/preprocessor codepath and step until you find the root cause. + +If the error looks like a preprocessing issue, note that we use Boost.Wave as the preprocessor; it can have quirky edge cases (e.g. needing a trailing newline/whitespace at the end of a file for correct parsing). + +## Best practices + +- Prefer compiling to a shader library (`-T lib_6_x`) and using multiple entry points when possible: fewer inputs means fewer compile rules and less build overhead; at runtime you still choose the entry point from the same `.spv`. +- Treat `CAPS` as a build-time cost multiplier (cartesian product). If the permutation count gets too large (thousands+), prebuilding usually stops paying off; an example of such workload is `examples_tests/23_Arithmetic2UnitTest`. + +## Complete example (expand) + +
+NSC rules + archive + runtime key usage + +NSC emits depfiles and the custom commands consume them, so changes in `#include`d HLSL files automatically trigger recompilation of the affected outputs. In most cases you do not need to list includes manually. Use `DEPENDS` only for extra inputs that are not discovered via `#include`. + +### CMake (`CMakeLists.txt`) + +```cmake +include(common) + +nbl_create_executable_project("" "" "" "") + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") + +set(JSON [=[ +[ + { + "INPUT": "app_resources/shader.hlsl", + "KEY": "shader", + "COMPILE_OPTIONS": ["-T", "lib_6_8"], + "CAPS": [ + { + "kind": "limits", + "name": "maxComputeSharedMemorySize", + "type": "uint32_t", + "values": [16384, 32768, 65536] + }, + { + "kind": "features", + "name": "shaderFloat64", + "type": "bool", + "values": [0, 1] + } + ] + } +] +]=]) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS -I ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +# Works for both NBL_EMBED_BUILTIN_RESOURCES=ON/OFF +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) +``` + +### Runtime usage (C++) + +```cpp +#include "nbl/this_example/builtin/build/spirv/keys.hpp" + +// Load relative to the VFS mount (examples mount it at "app_resources") +asset::IAssetLoader::SAssetLoadParams lp = {}; +lp.workingDirectory = "app_resources"; + +auto limits = device->getPhysicalDevice()->getLimits(); +limits.maxComputeSharedMemorySize = 32768; // one of the prebuilt values; real code should bucket/clamp with std::upper_bound (see the CountingSort snippet above) + +auto key = nbl::this_example::builtin::build::get_spirv_key<"shader">(limits, device->getEnabledFeatures()); +auto bundle = assetMgr->getAsset(key.c_str(), lp); +const auto assets = bundle.getContents(); +auto spvShader = asset::IAsset::castDown(assets[0]); + +// params.shader.shader = spvShader.get(); + +// If you compiled with `-T lib_6_x`, pick the entry point at pipeline creation time (e.g. `params.shader.entryPoint = "main";`). +``` + +
diff --git a/examples_tests b/examples_tests index dd7de7a89c..2b034eb4a7 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit dd7de7a89cfa5a59970dde4d4744ecf746d77a4a +Subproject commit 2b034eb4a796e043d882e9e6335070466e7a871f diff --git a/include/matrix3x4SIMD.h b/include/matrix3x4SIMD.h deleted file mode 100644 index d52f305cec..0000000000 --- a/include/matrix3x4SIMD.h +++ /dev/null @@ -1,263 +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_MATRIX3X4SIMD_H_INCLUDED__ -#define __NBL_MATRIX3X4SIMD_H_INCLUDED__ - -#include "vectorSIMD.h" -#include "quaternion.h" - -namespace nbl::core -{ - -class matrix4x3; - -#define _NBL_MATRIX_ALIGNMENT _NBL_SIMD_ALIGNMENT -static_assert(_NBL_MATRIX_ALIGNMENT>=_NBL_VECTOR_ALIGNMENT,"Matrix must be equally or more aligned than vector!"); - -//! Equivalent of GLSL's mat4x3 -class matrix3x4SIMD// : private AllocationOverrideBase<_NBL_MATRIX_ALIGNMENT> EBO inheritance problem w.r.t `rows[3]` -{ - public: - _NBL_STATIC_INLINE_CONSTEXPR uint32_t VectorCount = 3u; - vectorSIMDf rows[VectorCount]; - - explicit matrix3x4SIMD( const vectorSIMDf& _r0 = vectorSIMDf(1.f, 0.f, 0.f, 0.f), - const vectorSIMDf& _r1 = vectorSIMDf(0.f, 1.f, 0.f, 0.f), - const vectorSIMDf& _r2 = vectorSIMDf(0.f, 0.f, 1.f, 0.f)) : rows{_r0, _r1, _r2} - { - } - - matrix3x4SIMD( float _a00, float _a01, float _a02, float _a03, - float _a10, float _a11, float _a12, float _a13, - float _a20, float _a21, float _a22, float _a23) - : matrix3x4SIMD(vectorSIMDf(_a00, _a01, _a02, _a03), - vectorSIMDf(_a10, _a11, _a12, _a13), - vectorSIMDf(_a20, _a21, _a22, _a23)) - { - } - - explicit matrix3x4SIMD(const float* const _data) - { - if (!_data) - return; - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] = vectorSIMDf(_data + 4*i); - } - matrix3x4SIMD(const float* const _data, bool ALIGNED) - { - if (!_data) - return; - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] = vectorSIMDf(_data + 4*i, ALIGNED); - } - - float* pointer() { return rows[0].pointer; } - const float* pointer() const { return rows[0].pointer; } - - inline matrix3x4SIMD& set(const matrix4x3& _retarded); - inline matrix4x3 getAsRetardedIrrlichtMatrix() const; - - static inline matrix3x4SIMD concatenateBFollowedByA(const matrix3x4SIMD& _a, const matrix3x4SIMD& _b); - - static inline matrix3x4SIMD concatenateBFollowedByAPrecisely(const matrix3x4SIMD& _a, const matrix3x4SIMD& _b); - - inline matrix3x4SIMD& concatenateAfter(const matrix3x4SIMD& _other) - { - return *this = concatenateBFollowedByA(*this, _other); - } - - inline matrix3x4SIMD& concatenateBefore(const matrix3x4SIMD& _other) - { - return *this = concatenateBFollowedByA(_other, *this); - } - - inline matrix3x4SIMD& concatenateAfterPrecisely(const matrix3x4SIMD& _other) - { - return *this = concatenateBFollowedByAPrecisely(*this, _other); - } - - inline matrix3x4SIMD& concatenateBeforePrecisely(const matrix3x4SIMD& _other) - { - return *this = concatenateBFollowedByAPrecisely(_other, *this); - } - - inline bool operator==(const matrix3x4SIMD& _other) - { - return !(*this != _other); - } - - inline bool operator!=(const matrix3x4SIMD& _other); - - - inline matrix3x4SIMD operator-() const - { - matrix3x4SIMD retval; - retval.rows[0] = -rows[0]; - retval.rows[1] = -rows[1]; - retval.rows[2] = -rows[2]; - return retval; - } - - - inline matrix3x4SIMD& operator+=(const matrix3x4SIMD& _other); - inline matrix3x4SIMD operator+(const matrix3x4SIMD& _other) const - { - matrix3x4SIMD retval(*this); - return retval += _other; - } - - inline matrix3x4SIMD& operator-=(const matrix3x4SIMD& _other); - inline matrix3x4SIMD operator-(const matrix3x4SIMD& _other) const - { - matrix3x4SIMD retval(*this); - return retval -= _other; - } - - inline matrix3x4SIMD& operator*=(float _scalar); - inline matrix3x4SIMD operator*(float _scalar) const - { - matrix3x4SIMD retval(*this); - return retval *= _scalar; - } - - inline matrix3x4SIMD& setTranslation(const vectorSIMDf& _translation) - { - // no faster way of doing it? - rows[0].w = _translation.x; - rows[1].w = _translation.y; - rows[2].w = _translation.z; - return *this; - } - inline vectorSIMDf getTranslation() const; - inline vectorSIMDf getTranslation3D() const; - - inline matrix3x4SIMD& setScale(const vectorSIMDf& _scale); - - inline vectorSIMDf getScale() const; - - inline void transformVect(vectorSIMDf& _out, const vectorSIMDf& _in) const; - inline void transformVect(vectorSIMDf& _in_out) const - { - transformVect(_in_out, _in_out); - } - - inline void pseudoMulWith4x1(vectorSIMDf& _out, const vectorSIMDf& _in) const; - inline void pseudoMulWith4x1(vectorSIMDf& _in_out) const - { - pseudoMulWith4x1(_in_out,_in_out); - } - - inline void mulSub3x3WithNx1(vectorSIMDf& _out, const vectorSIMDf& _in) const; - inline void mulSub3x3WithNx1(vectorSIMDf& _in_out) const - { - mulSub3x3WithNx1(_in_out, _in_out); - } - - inline static matrix3x4SIMD buildCameraLookAtMatrixLH( - const vectorSIMDf& position, - const vectorSIMDf& target, - const vectorSIMDf& upVector); - inline static matrix3x4SIMD buildCameraLookAtMatrixRH( - const vectorSIMDf& position, - const vectorSIMDf& target, - const vectorSIMDf& upVector); - - inline matrix3x4SIMD& setRotation(const quaternion& _quat); - - inline matrix3x4SIMD& setScaleRotationAndTranslation( const vectorSIMDf& _scale, - const quaternion& _quat, - const vectorSIMDf& _translation); - - inline vectorSIMDf getPseudoDeterminant() const - { - vectorSIMDf tmp; - return determinant_helper(tmp); - } - - inline bool getInverse(matrix3x4SIMD& _out) const; - bool makeInverse() - { - matrix3x4SIMD tmp; - - if (getInverse(tmp)) - { - *this = tmp; - return true; - } - return false; - } - - // - inline bool getSub3x3InverseTranspose(matrix3x4SIMD& _out) const; - - // - inline bool getSub3x3InverseTransposePacked(float outRows[9]) const - { - matrix3x4SIMD tmp; - if (!getSub3x3InverseTranspose(tmp)) - return false; - - float* _out = outRows; - for (auto i=0; i<3; i++) - { - const auto& row = tmp.rows[i]; - for (auto j=0; j<3; j++) - *(_out++) = row[j]; - } - - return true; - } - - // - inline core::matrix3x4SIMD getSub3x3TransposeCofactors() const; - - // - inline void setTransformationCenter(const vectorSIMDf& _center, const vectorSIMDf& _translation); - - // - static inline matrix3x4SIMD buildAxisAlignedBillboard( - const vectorSIMDf& camPos, - const vectorSIMDf& center, - const vectorSIMDf& translation, - const vectorSIMDf& axis, - const vectorSIMDf& from); - - - // - float& operator()(size_t _i, size_t _j) { return rows[_i].pointer[_j]; } - const float& operator()(size_t _i, size_t _j) const { return rows[_i].pointer[_j]; } - - // - inline const vectorSIMDf& operator[](size_t _rown) const { return rows[_rown]; } - inline vectorSIMDf& operator[](size_t _rown) { return rows[_rown]; } - - private: - static inline vectorSIMDf doJob(const __m128& a, const matrix3x4SIMD& _mtx); - - // really need that dvec<2> or wider - inline __m128d halfRowAsDouble(size_t _n, bool _0) const; - static inline __m128d doJob_d(const __m128d& _a0, const __m128d& _a1, const matrix3x4SIMD& _mtx, bool _xyHalf); - - vectorSIMDf determinant_helper(vectorSIMDf& r1crossr2) const - { - r1crossr2 = core::cross(rows[1], rows[2]); - return core::dot(rows[0], r1crossr2); - } -}; - -inline matrix3x4SIMD concatenateBFollowedByA(const matrix3x4SIMD& _a, const matrix3x4SIMD& _b) -{ - return matrix3x4SIMD::concatenateBFollowedByA(_a, _b); -} -/* -inline matrix3x4SIMD concatenateBFollowedByAPrecisely(const matrix3x4SIMD& _a, const matrix3x4SIMD& _b) -{ - return matrix3x4SIMD::concatenateBFollowedByAPrecisely(_a, _b); -} -*/ - -} - -#endif diff --git a/include/matrix3x4SIMD_impl.h b/include/matrix3x4SIMD_impl.h deleted file mode 100644 index 0e9022efd0..0000000000 --- a/include/matrix3x4SIMD_impl.h +++ /dev/null @@ -1,470 +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_MATRIX3X4SIMD_IMPL_H_INCLUDED_ -#define _NBL_MATRIX3X4SIMD_IMPL_H_INCLUDED_ - -#include "matrix3x4SIMD.h" -#include "nbl/core/math/glslFunctions.tcc" - -namespace nbl::core -{ - -// TODO: move to another implementation header -inline quaternion::quaternion(const matrix3x4SIMD& m) -{ - const vectorSIMDf one(1.f); - auto Qx = m.rows[0].xxxx()^vectorSIMDu32(0,0,0x80000000u,0x80000000u); - auto Qy = m.rows[1].yyyy()^vectorSIMDu32(0,0x80000000u,0,0x80000000u); - auto Qz = m.rows[2].zzzz()^vectorSIMDu32(0,0x80000000u,0x80000000u,0); - - auto tmp = one+Qx+Qy+Qz; - auto invscales = inversesqrt(tmp)*0.5f; - auto scales = tmp*invscales*0.5f; - - // TODO: speed this up - if (tmp.x > 0.0f) - { - X = (m(2, 1) - m(1, 2)) * invscales.x; - Y = (m(0, 2) - m(2, 0)) * invscales.x; - Z = (m(1, 0) - m(0, 1)) * invscales.x; - W = scales.x; - } - else - { - if (tmp.y>0.f) - { - X = scales.y; - Y = (m(0, 1) + m(1, 0)) * invscales.y; - Z = (m(2, 0) + m(0, 2)) * invscales.y; - W = (m(2, 1) - m(1, 2)) * invscales.y; - } - else if (tmp.z>0.f) - { - X = (m(0, 1) + m(1, 0)) * invscales.z; - Y = scales.z; - Z = (m(1, 2) + m(2, 1)) * invscales.z; - W = (m(0, 2) - m(2, 0)) * invscales.z; - } - else - { - X = (m(0, 2) + m(2, 0)) * invscales.w; - Y = (m(1, 2) + m(2, 1)) * invscales.w; - Z = scales.w; - W = (m(1, 0) - m(0, 1)) * invscales.w; - } - } - - *this = normalize(*this); -} - -inline bool matrix3x4SIMD::operator!=(const matrix3x4SIMD& _other) -{ - for (size_t i = 0u; i < VectorCount; ++i) - if ((rows[i] != _other.rows[i]).any()) - return true; - return false; -} - -inline matrix3x4SIMD& matrix3x4SIMD::operator+=(const matrix3x4SIMD& _other) -{ - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] += _other.rows[i]; - return *this; -} -inline matrix3x4SIMD& matrix3x4SIMD::operator-=(const matrix3x4SIMD& _other) -{ - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] -= _other.rows[i]; - return *this; -} -inline matrix3x4SIMD& matrix3x4SIMD::operator*=(float _scalar) -{ - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] *= _scalar; - return *this; -} - -#ifdef __NBL_COMPILE_WITH_SSE3 -#define BROADCAST32(fpx) _MM_SHUFFLE(fpx, fpx, fpx, fpx) -#define BUILD_XORMASKF(_x_, _y_, _z_, _w_) _mm_setr_epi32(_x_ ? 0x80000000u:0x0u, _y_ ? 0x80000000u:0x0u, _z_ ? 0x80000000u:0x0u, _w_ ? 0x80000000u:0x0u) -#define BUILD_MASKF(_x_, _y_, _z_, _w_) _mm_setr_epi32(_x_*0xffffffff, _y_*0xffffffff, _z_*0xffffffff, _w_*0xffffffff) - -inline matrix3x4SIMD matrix3x4SIMD::concatenateBFollowedByA(const matrix3x4SIMD& _a, const matrix3x4SIMD& _b) -{ -#ifdef _NBL_DEBUG - assert(is_aligned_to(&_a, _NBL_SIMD_ALIGNMENT)); - assert(is_aligned_to(&_b, _NBL_SIMD_ALIGNMENT)); -#endif // _NBL_DEBUG - __m128 r0 = _a.rows[0].getAsRegister(); - __m128 r1 = _a.rows[1].getAsRegister(); - __m128 r2 = _a.rows[2].getAsRegister(); - - matrix3x4SIMD out; - out.rows[0] = matrix3x4SIMD::doJob(r0, _b); - out.rows[1] = matrix3x4SIMD::doJob(r1, _b); - out.rows[2] = matrix3x4SIMD::doJob(r2, _b); - - return out; -} - -inline matrix3x4SIMD matrix3x4SIMD::concatenateBFollowedByAPrecisely(const matrix3x4SIMD& _a, const matrix3x4SIMD& _b) -{ - __m128d r00 = _a.halfRowAsDouble(0u, true); - __m128d r01 = _a.halfRowAsDouble(0u, false); - __m128d r10 = _a.halfRowAsDouble(1u, true); - __m128d r11 = _a.halfRowAsDouble(1u, false); - __m128d r20 = _a.halfRowAsDouble(2u, true); - __m128d r21 = _a.halfRowAsDouble(2u, false); - - matrix3x4SIMD out; - - const __m128i mask0011 = BUILD_MASKF(0, 0, 1, 1); - - __m128 second = _mm_cvtpd_ps(matrix3x4SIMD::doJob_d(r00, r01, _b, false)); - out.rows[0] = vectorSIMDf(_mm_cvtpd_ps(matrix3x4SIMD::doJob_d(r00, r01, _b, true))) | _mm_castps_si128((vectorSIMDf(_mm_movelh_ps(second, second)) & mask0011).getAsRegister()); - - second = _mm_cvtpd_ps(matrix3x4SIMD::doJob_d(r10, r11, _b, false)); - out.rows[1] = vectorSIMDf(_mm_cvtpd_ps(matrix3x4SIMD::doJob_d(r10, r11, _b, true))) | _mm_castps_si128((vectorSIMDf(_mm_movelh_ps(second, second)) & mask0011).getAsRegister()); - - second = _mm_cvtpd_ps(matrix3x4SIMD::doJob_d(r20, r21, _b, false)); - out.rows[2] = vectorSIMDf(_mm_cvtpd_ps(matrix3x4SIMD::doJob_d(r20, r21, _b, true))) | _mm_castps_si128((vectorSIMDf(_mm_movelh_ps(second, second)) & mask0011).getAsRegister()); - - return out; -} - -inline vectorSIMDf matrix3x4SIMD::getTranslation() const -{ - __m128 xmm0 = _mm_unpackhi_ps(rows[0].getAsRegister(), rows[1].getAsRegister()); // (0z,1z,0w,1w) - __m128 xmm1 = _mm_unpackhi_ps(rows[2].getAsRegister(), _mm_setr_ps(0.f, 0.f, 0.f, 1.f)); // (2z,3z,2w,3w) - __m128 xmm2 = _mm_movehl_ps(xmm1, xmm0);// (0w,1w,2w,3w) - - return xmm2; -} -inline vectorSIMDf matrix3x4SIMD::getTranslation3D() const -{ - __m128 xmm0 = _mm_unpackhi_ps(rows[0].getAsRegister(), rows[1].getAsRegister()); // (0z,1z,0w,1w) - __m128 xmm1 = _mm_unpackhi_ps(rows[2].getAsRegister(), _mm_setzero_ps()); // (2z,0,2w,0) - __m128 xmm2 = _mm_movehl_ps(xmm1, xmm0);// (0w,1w,2w,0) - - return xmm2; -} - -inline matrix3x4SIMD& matrix3x4SIMD::setScale(const core::vectorSIMDf& _scale) -{ - const vectorSIMDu32 mask0001 = vectorSIMDu32(BUILD_MASKF(0, 0, 0, 1)); - const vectorSIMDu32 mask0010 = vectorSIMDu32(BUILD_MASKF(0, 0, 1, 0)); - const vectorSIMDu32 mask0100 = vectorSIMDu32(BUILD_MASKF(0, 1, 0, 0)); - const vectorSIMDu32 mask1000 = vectorSIMDu32(BUILD_MASKF(1, 0, 0, 0)); - - const vectorSIMDu32& scaleAlias = reinterpret_cast(_scale); - - vectorSIMDu32& rowAlias0 = reinterpret_cast(rows[0]); - vectorSIMDu32& rowAlias1 = reinterpret_cast(rows[1]); - vectorSIMDu32& rowAlias2 = reinterpret_cast(rows[2]); - rowAlias0 = (scaleAlias & reinterpret_cast(mask1000)) | (rowAlias0 & reinterpret_cast(mask0001)); - rowAlias1 = (scaleAlias & reinterpret_cast(mask0100)) | (rowAlias1 & reinterpret_cast(mask0001)); - rowAlias2 = (scaleAlias & reinterpret_cast(mask0010)) | (rowAlias2 & reinterpret_cast(mask0001)); - - return *this; -} - -inline core::vectorSIMDf matrix3x4SIMD::getScale() const -{ - // xmm4-7 will now become columuns of B - __m128 xmm4 = rows[0].getAsRegister(); - __m128 xmm5 = rows[1].getAsRegister(); - __m128 xmm6 = rows[2].getAsRegister(); - __m128 xmm7 = _mm_setzero_ps(); - // g==0 - __m128 xmm0 = _mm_unpacklo_ps(xmm4, xmm5); - __m128 xmm1 = _mm_unpacklo_ps(xmm6, xmm7); // (2x,g,2y,g) - __m128 xmm2 = _mm_unpackhi_ps(xmm4, xmm5); - __m128 xmm3 = _mm_unpackhi_ps(xmm6, xmm7); // (2z,g,2w,g) - xmm4 = _mm_movelh_ps(xmm1, xmm0); //(0x,1x,2x,g) - xmm5 = _mm_movehl_ps(xmm1, xmm0); - xmm6 = _mm_movelh_ps(xmm3, xmm2); //(0z,1z,2z,g) - - // See http://www.robertblum.com/articles/2005/02/14/decomposing-matrices - // We have to do the full calculation. - xmm0 = _mm_mul_ps(xmm4, xmm4);// column 0 squared - xmm1 = _mm_mul_ps(xmm5, xmm5);// column 1 squared - xmm2 = _mm_mul_ps(xmm6, xmm6);// column 2 squared - xmm4 = _mm_hadd_ps(xmm0, xmm1); - xmm5 = _mm_hadd_ps(xmm2, xmm7); - xmm6 = _mm_hadd_ps(xmm4, xmm5); - - return _mm_sqrt_ps(xmm6); -} - -inline void matrix3x4SIMD::transformVect(vectorSIMDf& _out, const vectorSIMDf& _in) const -{ - vectorSIMDf r0 = rows[0] * _in, - r1 = rows[1] * _in, - r2 = rows[2] * _in; - - _out = - _mm_hadd_ps( - _mm_hadd_ps(r0.getAsRegister(), r1.getAsRegister()), - _mm_hadd_ps(r2.getAsRegister(), _mm_set1_ps(0.25f)) - ); -} - -inline void matrix3x4SIMD::pseudoMulWith4x1(vectorSIMDf& _out, const vectorSIMDf& _in) const -{ - __m128i mask1110 = BUILD_MASKF(1, 1, 1, 0); - _out = (_in & mask1110) | _mm_castps_si128(vectorSIMDf(0.f, 0.f, 0.f, 1.f).getAsRegister()); - transformVect(_out); -} - -inline void matrix3x4SIMD::mulSub3x3WithNx1(vectorSIMDf& _out, const vectorSIMDf& _in) const -{ - auto maskedIn = _in & BUILD_MASKF(1, 1, 1, 0); - vectorSIMDf r0 = rows[0] * maskedIn, - r1 = rows[1] * maskedIn, - r2 = rows[2] * maskedIn; - - _out = - _mm_hadd_ps( - _mm_hadd_ps(r0.getAsRegister(), r1.getAsRegister()), - _mm_hadd_ps(r2.getAsRegister(), _mm_setzero_ps()) - ); -} - - -inline matrix3x4SIMD matrix3x4SIMD::buildCameraLookAtMatrixLH( - const core::vectorSIMDf& position, - const core::vectorSIMDf& target, - const core::vectorSIMDf& upVector) -{ - const core::vectorSIMDf zaxis = core::normalize(target - position); - const core::vectorSIMDf xaxis = core::normalize(core::cross(upVector, zaxis)); - const core::vectorSIMDf yaxis = core::cross(zaxis, xaxis); - - matrix3x4SIMD r; - r.rows[0] = xaxis; - r.rows[1] = yaxis; - r.rows[2] = zaxis; - r.rows[0].w = -dot(xaxis, position)[0]; - r.rows[1].w = -dot(yaxis, position)[0]; - r.rows[2].w = -dot(zaxis, position)[0]; - - return r; -} -inline matrix3x4SIMD matrix3x4SIMD::buildCameraLookAtMatrixRH( - const core::vectorSIMDf& position, - const core::vectorSIMDf& target, - const core::vectorSIMDf& upVector) -{ - const core::vectorSIMDf zaxis = core::normalize(position - target); - const core::vectorSIMDf xaxis = core::normalize(core::cross(upVector, zaxis)); - const core::vectorSIMDf yaxis = core::cross(zaxis, xaxis); - - matrix3x4SIMD r; - r.rows[0] = xaxis; - r.rows[1] = yaxis; - r.rows[2] = zaxis; - r.rows[0].w = -dot(xaxis, position)[0]; - r.rows[1].w = -dot(yaxis, position)[0]; - r.rows[2].w = -dot(zaxis, position)[0]; - - return r; -} - -inline matrix3x4SIMD& matrix3x4SIMD::setRotation(const core::quaternion& _quat) -{ - const vectorSIMDu32 mask0001 = vectorSIMDu32(BUILD_MASKF(0, 0, 0, 1)); - const __m128i mask1110 = BUILD_MASKF(1, 1, 1, 0); - - const core::vectorSIMDf& quat = reinterpret_cast(_quat); - rows[0] = ((quat.yyyy() * ((quat.yxwx() & mask1110) * vectorSIMDf(2.f))) + (quat.zzzz() * (quat.zwxx() & mask1110) * vectorSIMDf(2.f, -2.f, 2.f, 0.f))) | (reinterpret_cast(rows[0]) & (mask0001)); - rows[0].x = 1.f - rows[0].x; - - rows[1] = ((quat.zzzz() * ((quat.wzyx() & mask1110) * vectorSIMDf(2.f))) + (quat.xxxx() * (quat.yxwx() & mask1110) * vectorSIMDf(2.f, 2.f, -2.f, 0.f))) | (reinterpret_cast(rows[1]) & (mask0001)); - rows[1].y = 1.f - rows[1].y; - - rows[2] = ((quat.xxxx() * ((quat.zwxx() & mask1110) * vectorSIMDf(2.f))) + (quat.yyyy() * (quat.wzyx() & mask1110) * vectorSIMDf(-2.f, 2.f, 2.f, 0.f))) | (reinterpret_cast(rows[2]) & (mask0001)); - rows[2].z = 1.f - rows[2].z; - - return *this; -} - -inline matrix3x4SIMD& matrix3x4SIMD::setScaleRotationAndTranslation(const vectorSIMDf& _scale, const core::quaternion& _quat, const vectorSIMDf& _translation) -{ - const __m128i mask1110 = BUILD_MASKF(1, 1, 1, 0); - - const vectorSIMDf& quat = reinterpret_cast(_quat); - const vectorSIMDf dblScale = (_scale * 2.f) & mask1110; - - vectorSIMDf mlt = dblScale ^ BUILD_XORMASKF(0, 1, 0, 0); - rows[0] = ((quat.yyyy() * ((quat.yxwx() & mask1110) * dblScale)) + (quat.zzzz() * (quat.zwxx() & mask1110) * mlt)); - rows[0].x = _scale.x - rows[0].x; - - mlt = dblScale ^ BUILD_XORMASKF(0, 0, 1, 0); - rows[1] = ((quat.zzzz() * ((quat.wzyx() & mask1110) * dblScale)) + (quat.xxxx() * (quat.yxwx() & mask1110) * mlt)); - rows[1].y = _scale.y - rows[1].y; - - mlt = dblScale ^ BUILD_XORMASKF(1, 0, 0, 0); - rows[2] = ((quat.xxxx() * ((quat.zwxx() & mask1110) * dblScale)) + (quat.yyyy() * (quat.wzyx() & mask1110) * mlt)); - rows[2].z = _scale.z - rows[2].z; - - setTranslation(_translation); - - return *this; -} - - -inline bool matrix3x4SIMD::getInverse(matrix3x4SIMD& _out) const //! SUBOPTIMAL - OPTIMIZE! -{ - auto translation = getTranslation(); - // `tmp` will have columns in its `rows` - core::matrix4SIMD tmp; - auto* cols = tmp.rows; - if (!getSub3x3InverseTranspose(reinterpret_cast(tmp))) - return false; - - // find inverse post-translation - cols[3] = -cols[0]*translation.xxxx()-cols[1]*translation.yyyy()-cols[2]*translation.zzzz(); - - // columns into rows - _out = transpose(tmp).extractSub3x4(); - - return true; -} - -inline bool matrix3x4SIMD::getSub3x3InverseTranspose(core::matrix3x4SIMD& _out) const -{ - vectorSIMDf r1crossr2; - const vectorSIMDf d = determinant_helper(r1crossr2); - if (core::iszero(d.x, FLT_MIN)) - return false; - auto rcp = core::reciprocal(d); - - // matrix of cofactors * 1/det - _out = getSub3x3TransposeCofactors(); - _out.rows[0] *= rcp; - _out.rows[1] *= rcp; - _out.rows[2] *= rcp; - - return true; -} - -inline core::matrix3x4SIMD matrix3x4SIMD::getSub3x3TransposeCofactors() const -{ - core::matrix3x4SIMD _out; - _out.rows[0] = core::cross(rows[1], rows[2]); - _out.rows[1] = core::cross(rows[2], rows[0]); - _out.rows[2] = core::cross(rows[0], rows[1]); - return _out; -} - -// TODO: Double check this!- -inline void matrix3x4SIMD::setTransformationCenter(const core::vectorSIMDf& _center, const core::vectorSIMDf& _translation) -{ - core::vectorSIMDf r0 = rows[0] * _center; - core::vectorSIMDf r1 = rows[1] * _center; - core::vectorSIMDf r2 = rows[2] * _center; - core::vectorSIMDf r3(0.f, 0.f, 0.f, 1.f); - - __m128 col3 = _mm_hadd_ps(_mm_hadd_ps(r0.getAsRegister(), r1.getAsRegister()), _mm_hadd_ps(r2.getAsRegister(), r3.getAsRegister())); - const vectorSIMDf vcol3 = _center - _translation - col3; - - for (size_t i = 0u; i < VectorCount; ++i) - rows[i].w = vcol3.pointer[i]; -} - - -// TODO: Double check this! -inline matrix3x4SIMD matrix3x4SIMD::buildAxisAlignedBillboard( - const core::vectorSIMDf& camPos, - const core::vectorSIMDf& center, - const core::vectorSIMDf& translation, - const core::vectorSIMDf& axis, - const core::vectorSIMDf& from) -{ - // axis of rotation - const core::vectorSIMDf up = core::normalize(axis); - const core::vectorSIMDf forward = core::normalize(camPos - center); - const core::vectorSIMDf right = core::normalize(core::cross(up, forward)); - - // correct look vector - const core::vectorSIMDf look = core::cross(right, up); - - // rotate from to - // axis multiplication by sin - const core::vectorSIMDf vs = core::cross(look, from); - - // cosinus angle - const core::vectorSIMDf ca = core::cross(from, look); - - const core::vectorSIMDf vt(up * (core::vectorSIMDf(1.f) - ca)); - const core::vectorSIMDf wt = vt * up.yzxx(); - const core::vectorSIMDf vtuppca = vt * up + ca; - - matrix3x4SIMD mat; - core::vectorSIMDf& row0 = mat.rows[0]; - core::vectorSIMDf& row1 = mat.rows[1]; - core::vectorSIMDf& row2 = mat.rows[2]; - - row0 = vtuppca & BUILD_MASKF(1, 0, 0, 0); - row1 = vtuppca & BUILD_MASKF(0, 1, 0, 0); - row2 = vtuppca & BUILD_MASKF(0, 0, 1, 0); - - row0 += (wt.xxzx() + vs.xzyx() * core::vectorSIMDf(1.f, 1.f, -1.f, 1.f)) & BUILD_MASKF(0, 1, 1, 0); - row1 += (wt.xxyx() + vs.zxxx() * core::vectorSIMDf(-1.f, 1.f, 1.f, 1.f)) & BUILD_MASKF(1, 0, 1, 0); - row2 += (wt.zyxx() + vs.yxxx() * core::vectorSIMDf(1.f, -1.f, 1.f, 1.f)) & BUILD_MASKF(1, 1, 0, 0); - - mat.setTransformationCenter(center, translation); - return mat; -} - - - -inline vectorSIMDf matrix3x4SIMD::doJob(const __m128& a, const matrix3x4SIMD& _mtx) -{ - __m128 r0 = _mtx.rows[0].getAsRegister(); - __m128 r1 = _mtx.rows[1].getAsRegister(); - __m128 r2 = _mtx.rows[2].getAsRegister(); - - const __m128i mask = _mm_setr_epi32(0, 0, 0, 0xffffffff); - - vectorSIMDf res; - res = _mm_mul_ps(_mm_shuffle_ps(a, a, BROADCAST32(0)), r0); - res += _mm_mul_ps(_mm_shuffle_ps(a, a, BROADCAST32(1)), r1); - res += _mm_mul_ps(_mm_shuffle_ps(a, a, BROADCAST32(2)), r2); - res += vectorSIMDf(a) & mask; // always 0 0 0 a3 -- no shuffle needed - return res; - } - -inline __m128d matrix3x4SIMD::halfRowAsDouble(size_t _n, bool _0) const -{ - return _mm_cvtps_pd(_0 ? rows[_n].xyxx().getAsRegister() : rows[_n].zwxx().getAsRegister()); -} -inline __m128d matrix3x4SIMD::doJob_d(const __m128d& _a0, const __m128d& _a1, const matrix3x4SIMD& _mtx, bool _xyHalf) -{ - __m128d r0 = _mtx.halfRowAsDouble(0u, _xyHalf); - __m128d r1 = _mtx.halfRowAsDouble(1u, _xyHalf); - __m128d r2 = _mtx.halfRowAsDouble(2u, _xyHalf); - - const __m128d mask01 = _mm_castsi128_pd(_mm_setr_epi32(0, 0, 0xffffffff, 0xffffffff)); - - __m128d res; - res = _mm_mul_pd(_mm_shuffle_pd(_a0, _a0, 0), r0); - res = _mm_add_pd(res, _mm_mul_pd(_mm_shuffle_pd(_a0, _a0, 3), r1)); - res = _mm_add_pd(res, _mm_mul_pd(_mm_shuffle_pd(_a1, _a1, 0), r2)); - if (!_xyHalf) - res = _mm_add_pd(res, _mm_and_pd(_a1, mask01)); - return res; -} - -#undef BUILD_MASKF -#undef BUILD_XORMASKF -#undef BROADCAST32 -#else -#error "no implementation" -#endif - -} // nbl::core - -#endif diff --git a/include/matrix4SIMD.h b/include/matrix4SIMD.h deleted file mode 100644 index 03126c61f7..0000000000 --- a/include/matrix4SIMD.h +++ /dev/null @@ -1,385 +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_MATRIX4SIMD_H_INCLUDED__ -#define __NBL_MATRIX4SIMD_H_INCLUDED__ - -#include "matrix3x4SIMD.h" - -namespace nbl -{ -namespace core -{ - -template -class aabbox3d; - - -class matrix4SIMD// : public AlignedBase<_NBL_SIMD_ALIGNMENT> don't inherit from AlignedBase (which is empty) because member `rows[4]` inherits from it as well -{ - public: - _NBL_STATIC_INLINE_CONSTEXPR uint32_t VectorCount = 4u; - vectorSIMDf rows[VectorCount]; - - inline explicit matrix4SIMD(const vectorSIMDf& _r0 = vectorSIMDf(1.f, 0.f, 0.f, 0.f), - const vectorSIMDf& _r1 = vectorSIMDf(0.f, 1.f, 0.f, 0.f), - const vectorSIMDf& _r2 = vectorSIMDf(0.f, 0.f, 1.f, 0.f), - const vectorSIMDf& _r3 = vectorSIMDf(0.f, 0.f, 0.f, 1.f)) - : rows{ _r0, _r1, _r2, _r3 } - { - } - - inline matrix4SIMD( float _a00, float _a01, float _a02, float _a03, - float _a10, float _a11, float _a12, float _a13, - float _a20, float _a21, float _a22, float _a23, - float _a30, float _a31, float _a32, float _a33) - : matrix4SIMD( vectorSIMDf(_a00, _a01, _a02, _a03), - vectorSIMDf(_a10, _a11, _a12, _a13), - vectorSIMDf(_a20, _a21, _a22, _a23), - vectorSIMDf(_a30, _a31, _a32, _a33)) - { - } - - inline explicit matrix4SIMD(const float* const _data) - { - if (!_data) - return; - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] = vectorSIMDf(_data + 4 * i); - } - inline matrix4SIMD(const float* const _data, bool ALIGNED) - { - if (!_data) - return; - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] = vectorSIMDf(_data + 4 * i, ALIGNED); - } - - inline explicit matrix4SIMD(const matrix3x4SIMD& smallMat) - { - *reinterpret_cast(this) = smallMat; - rows[3].set(0.f,0.f,0.f,1.f); - } - - inline matrix3x4SIMD extractSub3x4() const - { - return matrix3x4SIMD(rows[0],rows[1],rows[2]); - } - - //! Access by row - inline const vectorSIMDf& getRow(size_t _rown) const{ return rows[_rown]; } - inline vectorSIMDf& getRow(size_t _rown) { return rows[_rown]; } - - //! Access by element - inline float operator()(size_t _i, size_t _j) const { return rows[_i].pointer[_j]; } - inline float& operator()(size_t _i, size_t _j) { return rows[_i].pointer[_j]; } - - //! Access for memory - inline const float* pointer() const {return rows[0].pointer;} - inline float* pointer() {return rows[0].pointer;} - - - inline bool operator==(const matrix4SIMD& _other) const - { - return !(*this != _other); - } - inline bool operator!=(const matrix4SIMD& _other) const; - - inline matrix4SIMD& operator+=(const matrix4SIMD& _other); - inline matrix4SIMD operator+(const matrix4SIMD& _other) const - { - matrix4SIMD r{*this}; - return r += _other; - } - - inline matrix4SIMD& operator-=(const matrix4SIMD& _other); - inline matrix4SIMD operator-(const matrix4SIMD& _other) const - { - matrix4SIMD r{*this}; - return r -= _other; - } - - inline matrix4SIMD& operator*=(float _scalar); - inline matrix4SIMD operator*(float _scalar) const - { - matrix4SIMD r{*this}; - return r *= _scalar; - } - - static inline matrix4SIMD concatenateBFollowedByA(const matrix4SIMD& _a, const matrix4SIMD& _b); - static inline matrix4SIMD concatenateBFollowedByAPrecisely(const matrix4SIMD& _a, const matrix4SIMD& _b); - - inline bool isIdentity() const - { - return *this == matrix4SIMD(); - } - inline bool isIdentity(float _tolerance) const; - - inline bool isOrthogonal() const - { - return concatenateBFollowedByA(transpose(*this), *this).isIdentity(); - } - inline bool isOrthogonal(float _tolerance) const - { - return concatenateBFollowedByA(transpose(*this), *this).isIdentity(_tolerance); - } - - inline matrix4SIMD& setScale(const core::vectorSIMDf& _scale); - inline matrix4SIMD& setScale(float _scale) - { - return setScale(vectorSIMDf(_scale)); - } - - inline void setTranslation(const float* _t) - { - for (size_t i = 0u; i < 3u; ++i) - rows[i].w = _t[i]; - } - //! Takes into account only x,y,z components of _t - inline void setTranslation(const vectorSIMDf& _t) - { - setTranslation(_t.pointer); - } - inline void setTranslation(const vector3d& _t) - { - setTranslation(&_t.X); - } - - //! Returns last column of the matrix. - inline vectorSIMDf getTranslation() const; - - //! Returns translation part of the matrix (w component is always 0). - inline vectorSIMDf getTranslation3D() const; - - enum class E_MATRIX_INVERSE_PRECISION - { - EMIP_FAST_RECIPROCAL, - EMIP_32BIT, - EMIP_64BBIT - }; - - template - inline bool getInverseTransform(matrix4SIMD& _out) const - { - if constexpr (precision == E_MATRIX_INVERSE_PRECISION::EMIP_64BBIT) - { - double a = rows[0][0], b = rows[0][1], c = rows[0][2], d = rows[0][3]; - double e = rows[1][0], f = rows[1][1], g = rows[1][2], h = rows[1][3]; - double i = rows[2][0], j = rows[2][1], k = rows[2][2], l = rows[2][3]; - double m = rows[3][0], n = rows[3][1], o = rows[3][2], p = rows[3][3]; - - double kp_lo = k * p - l * o; - double jp_ln = j * p - l * n; - double jo_kn = j * o - k * n; - double ip_lm = i * p - l * m; - double io_km = i * o - k * m; - double in_jm = i * n - j * m; - - double a11 = +(f * kp_lo - g * jp_ln + h * jo_kn); - double a12 = -(e * kp_lo - g * ip_lm + h * io_km); - double a13 = +(e * jp_ln - f * ip_lm + h * in_jm); - double a14 = -(e * jo_kn - f * io_km + g * in_jm); - - double det = a * a11 + b * a12 + c * a13 + d * a14; - - if (core::iszero(det, DBL_MIN)) - return false; - - double invDet = 1.0 / det; - - _out.rows[0][0] = a11 * invDet; - _out.rows[1][0] = a12 * invDet; - _out.rows[2][0] = a13 * invDet; - _out.rows[3][0] = a14 * invDet; - - _out.rows[0][1] = -(b * kp_lo - c * jp_ln + d * jo_kn) * invDet; - _out.rows[1][1] = +(a * kp_lo - c * ip_lm + d * io_km) * invDet; - _out.rows[2][1] = -(a * jp_ln - b * ip_lm + d * in_jm) * invDet; - _out.rows[3][1] = +(a * jo_kn - b * io_km + c * in_jm) * invDet; - - double gp_ho = g * p - h * o; - double fp_hn = f * p - h * n; - double fo_gn = f * o - g * n; - double ep_hm = e * p - h * m; - double eo_gm = e * o - g * m; - double en_fm = e * n - f * m; - - _out.rows[0][2] = +(b * gp_ho - c * fp_hn + d * fo_gn) * invDet; - _out.rows[1][2] = -(a * gp_ho - c * ep_hm + d * eo_gm) * invDet; - _out.rows[2][2] = +(a * fp_hn - b * ep_hm + d * en_fm) * invDet; - _out.rows[3][2] = -(a * fo_gn - b * eo_gm + c * en_fm) * invDet; - - double gl_hk = g * l - h * k; - double fl_hj = f * l - h * j; - double fk_gj = f * k - g * j; - double el_hi = e * l - h * i; - double ek_gi = e * k - g * i; - double ej_fi = e * j - f * i; - - _out.rows[0][3] = -(b * gl_hk - c * fl_hj + d * fk_gj) * invDet; - _out.rows[1][3] = +(a * gl_hk - c * el_hi + d * ek_gi) * invDet; - _out.rows[2][3] = -(a * fl_hj - b * el_hi + d * ej_fi) * invDet; - _out.rows[3][3] = +(a * fk_gj - b * ek_gi + c * ej_fi) * invDet; - - return true; - } - else - { - auto mat2mul = [](vectorSIMDf _A, vectorSIMDf _B) - { - return _A*_B.xwxw()+_A.yxwz()*_B.zyzy(); - }; - auto mat2adjmul = [](vectorSIMDf _A, vectorSIMDf _B) - { - return _A.wwxx()*_B-_A.yyzz()*_B.zwxy(); - }; - auto mat2muladj = [](vectorSIMDf _A, vectorSIMDf _B) - { - return _A*_B.wxwx()-_A.yxwz()*_B.zyzy(); - }; - - vectorSIMDf A = _mm_movelh_ps(rows[0].getAsRegister(), rows[1].getAsRegister()); - vectorSIMDf B = _mm_movehl_ps(rows[1].getAsRegister(), rows[0].getAsRegister()); - vectorSIMDf C = _mm_movelh_ps(rows[2].getAsRegister(), rows[3].getAsRegister()); - vectorSIMDf D = _mm_movehl_ps(rows[3].getAsRegister(), rows[2].getAsRegister()); - - vectorSIMDf allDets = vectorSIMDf(_mm_shuffle_ps(rows[0].getAsRegister(),rows[2].getAsRegister(),_MM_SHUFFLE(2,0,2,0)))* - vectorSIMDf(_mm_shuffle_ps(rows[1].getAsRegister(),rows[3].getAsRegister(),_MM_SHUFFLE(3,1,3,1))) - - - vectorSIMDf(_mm_shuffle_ps(rows[0].getAsRegister(),rows[2].getAsRegister(),_MM_SHUFFLE(3,1,3,1)))* - vectorSIMDf(_mm_shuffle_ps(rows[1].getAsRegister(),rows[3].getAsRegister(),_MM_SHUFFLE(2,0,2,0))); - - auto detA = allDets.xxxx(); - auto detB = allDets.yyyy(); - auto detC = allDets.zzzz(); - auto detD = allDets.wwww(); - - // https://lxjk.github.io/2017/09/03/Fast-4x4-Matrix-Inverse-with-SSE-SIMD-Explained.html - auto D_C = mat2adjmul(D, C); - // A#B - auto A_B = mat2adjmul(A, B); - // X# = |D|A - B(D#C) - auto X_ = detD*A - mat2mul(B, D_C); - // W# = |A|D - C(A#B) - auto W_ = detA*D - mat2mul(C, A_B); - - // |M| = |A|*|D| + ... (continue later) - auto detM = detA*detD; - - // Y# = |B|C - D(A#B)# - auto Y_ = detB*C - mat2muladj(D, A_B); - // Z# = |C|B - A(D#C)# - auto Z_ = detC*B - mat2muladj(A, D_C); - - // |M| = |A|*|D| + |B|*|C| ... (continue later) - detM += detB*detC; - - // tr((A#B)(D#C)) - __m128 tr = (A_B*D_C.xzyw()).getAsRegister(); - tr = _mm_hadd_ps(tr, tr); - tr = _mm_hadd_ps(tr, tr); - // |M| = |A|*|D| + |B|*|C| - tr((A#B)(D#C) - detM -= tr; - - if (core::iszero(detM.x, FLT_MIN)) - return false; - - vectorSIMDf rDetM; - - // (1/|M|, -1/|M|, -1/|M|, 1/|M|) - if constexpr (precision == E_MATRIX_INVERSE_PRECISION::EMIP_FAST_RECIPROCAL) - rDetM = vectorSIMDf(1.f, -1.f, -1.f, 1.f)*core::reciprocal(detM); - else if constexpr (precision == E_MATRIX_INVERSE_PRECISION::EMIP_32BIT) - rDetM = vectorSIMDf(1.f, -1.f, -1.f, 1.f).preciseDivision(detM); - - X_ *= rDetM; - Y_ *= rDetM; - Z_ *= rDetM; - W_ *= rDetM; - - // apply adjugate and store, here we combine adjugate shuffle and store shuffle - _out.rows[0] = _mm_shuffle_ps(X_.getAsRegister(), Y_.getAsRegister(), _MM_SHUFFLE(1, 3, 1, 3)); - _out.rows[1] = _mm_shuffle_ps(X_.getAsRegister(), Y_.getAsRegister(), _MM_SHUFFLE(0, 2, 0, 2)); - _out.rows[2] = _mm_shuffle_ps(Z_.getAsRegister(), W_.getAsRegister(), _MM_SHUFFLE(1, 3, 1, 3)); - _out.rows[3] = _mm_shuffle_ps(Z_.getAsRegister(), W_.getAsRegister(), _MM_SHUFFLE(0, 2, 0, 2)); - - return true; - } - } - - inline vectorSIMDf sub3x3TransformVect(const vectorSIMDf& _in) const; - - inline void transformVect(vectorSIMDf& _out, const vectorSIMDf& _in) const; - inline void transformVect(vectorSIMDf& _vector) const - { - transformVect(_vector, _vector); - } - - inline void translateVect(vectorSIMDf& _vect) const - { - _vect += getTranslation(); - } - - bool isBoxInFrustum(const aabbox3d& bbox); - - bool perspectiveTransformVect(core::vectorSIMDf& inOutVec) - { - transformVect(inOutVec); - const bool inFront = inOutVec[3] > 0.f; - inOutVec /= inOutVec.wwww(); - return inFront; - } - - core::vector2di fragCoordTransformVect(const core::vectorSIMDf& _in, const core::dimension2du& viewportDimensions) - { - core::vectorSIMDf pos(_in); - pos.w = 1.f; - if (perspectiveTransformVect(pos)) - core::vector2di(-0x80000000, -0x80000000); - - pos[0] *= 0.5f; - pos[1] *= 0.5f; - pos[0] += 0.5f; - pos[1] += 0.5f; - - return core::vector2di(pos[0] * float(viewportDimensions.Width), pos[1] * float(viewportDimensions.Height)); - } - - static inline matrix4SIMD buildProjectionMatrixPerspectiveFovRH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar); - static inline matrix4SIMD buildProjectionMatrixPerspectiveFovLH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar); - - static inline matrix4SIMD buildProjectionMatrixOrthoRH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar); - static inline matrix4SIMD buildProjectionMatrixOrthoLH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar); - - //! Access by row - inline const vectorSIMDf& operator[](size_t _rown) const { return rows[_rown]; } - //! Access by row - inline vectorSIMDf& operator[](size_t _rown) { return rows[_rown]; } - - private: - //! TODO: implement a dvec<2> - inline __m128d halfRowAsDouble(size_t _n, bool _firstHalf) const; - static inline __m128d concat64_helper(const __m128d& _a0, const __m128d& _a1, const matrix4SIMD& _mtx, bool _firstHalf); -}; - -inline matrix4SIMD operator*(float _scalar, const matrix4SIMD& _mtx) -{ - return _mtx * _scalar; -} - -inline matrix4SIMD concatenateBFollowedByA(const matrix4SIMD& _a, const matrix4SIMD& _b) -{ - return matrix4SIMD::concatenateBFollowedByA(_a, _b); -} -/* -inline matrix4SIMD concatenateBFollowedByAPrecisely(const matrix4SIMD& _a, const matrix4SIMD& _b) -{ - return matrix4SIMD::concatenateBFollowedByAPrecisely(_a, _b); -} -*/ - - -}} // nbl::core - -#endif diff --git a/include/matrix4SIMD_impl.h b/include/matrix4SIMD_impl.h deleted file mode 100644 index 02484e7a4c..0000000000 --- a/include/matrix4SIMD_impl.h +++ /dev/null @@ -1,299 +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_MATRIX4SIMD_IMPL_H_INCLUDED__ -#define __NBL_MATRIX4SIMD_IMPL_H_INCLUDED__ - -#include "matrix4SIMD.h" -#include "nbl/core/math/glslFunctions.tcc" -#include "aabbox3d.h" - -namespace nbl -{ -namespace core -{ - - -inline bool matrix4SIMD::operator!=(const matrix4SIMD& _other) const -{ - for (size_t i = 0u; i < VectorCount; ++i) - if ((rows[i] != _other.rows[i]).any()) - return true; - return false; -} - -inline matrix4SIMD& matrix4SIMD::operator+=(const matrix4SIMD& _other) -{ - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] += _other.rows[i]; - return *this; -} - -inline matrix4SIMD& matrix4SIMD::operator-=(const matrix4SIMD& _other) -{ - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] -= _other.rows[i]; - return *this; -} - -inline matrix4SIMD& matrix4SIMD::operator*=(float _scalar) -{ - for (size_t i = 0u; i < VectorCount; ++i) - rows[i] *= _scalar; - return *this; -} - -inline bool matrix4SIMD::isIdentity(float _tolerance) const -{ - return core::equals(*this, matrix4SIMD(), core::ROUNDING_ERROR()); -} - -#ifdef __NBL_COMPILE_WITH_SSE3 -#define BROADCAST32(fpx) _MM_SHUFFLE(fpx, fpx, fpx, fpx) -#define BUILD_MASKF(_x_, _y_, _z_, _w_) _mm_setr_epi32(_x_*0xffffffff, _y_*0xffffffff, _z_*0xffffffff, _w_*0xffffffff) -inline matrix4SIMD matrix4SIMD::concatenateBFollowedByA(const matrix4SIMD& _a, const matrix4SIMD& _b) -{ - auto calcRow = [](const __m128& _row, const matrix4SIMD& _mtx) - { - __m128 r0 = _mtx.rows[0].getAsRegister(); - __m128 r1 = _mtx.rows[1].getAsRegister(); - __m128 r2 = _mtx.rows[2].getAsRegister(); - __m128 r3 = _mtx.rows[3].getAsRegister(); - - __m128 res; - res = _mm_mul_ps(_mm_shuffle_ps(_row, _row, BROADCAST32(0)), r0); - res = _mm_add_ps(res, _mm_mul_ps(_mm_shuffle_ps(_row, _row, BROADCAST32(1)), r1)); - res = _mm_add_ps(res, _mm_mul_ps(_mm_shuffle_ps(_row, _row, BROADCAST32(2)), r2)); - res = _mm_add_ps(res, _mm_mul_ps(_mm_shuffle_ps(_row, _row, BROADCAST32(3)), r3)); - return res; - }; - - matrix4SIMD r; - for (size_t i = 0u; i < 4u; ++i) - r.rows[i] = calcRow(_a.rows[i].getAsRegister(), _b); - - return r; -} -inline matrix4SIMD matrix4SIMD::concatenateBFollowedByAPrecisely(const matrix4SIMD& _a, const matrix4SIMD& _b) -{ - matrix4SIMD out; - - __m128i mask0011 = BUILD_MASKF(0, 0, 1, 1); - __m128 second; - - { - __m128d r00 = _a.halfRowAsDouble(0u, true); - __m128d r01 = _a.halfRowAsDouble(0u, false); - second = _mm_cvtpd_ps(concat64_helper(r00, r01, _b, false)); - out.rows[0] = vectorSIMDf(_mm_cvtpd_ps(concat64_helper(r00, r01, _b, true))) | _mm_castps_si128((vectorSIMDf(_mm_movelh_ps(second, second)) & mask0011).getAsRegister()); - } - - { - __m128d r10 = _a.halfRowAsDouble(1u, true); - __m128d r11 = _a.halfRowAsDouble(1u, false); - second = _mm_cvtpd_ps(concat64_helper(r10, r11, _b, false)); - out.rows[1] = vectorSIMDf(_mm_cvtpd_ps(concat64_helper(r10, r11, _b, true))) | _mm_castps_si128((vectorSIMDf(_mm_movelh_ps(second, second)) & mask0011).getAsRegister()); - } - - { - __m128d r20 = _a.halfRowAsDouble(2u, true); - __m128d r21 = _a.halfRowAsDouble(2u, false); - second = _mm_cvtpd_ps(concat64_helper(r20, r21, _b, false)); - out.rows[2] = vectorSIMDf(_mm_cvtpd_ps(concat64_helper(r20, r21, _b, true))) | _mm_castps_si128((vectorSIMDf(_mm_movelh_ps(second, second)) & mask0011).getAsRegister()); - } - - { - __m128d r30 = _a.halfRowAsDouble(3u, true); - __m128d r31 = _a.halfRowAsDouble(3u, false); - second = _mm_cvtpd_ps(concat64_helper(r30, r31, _b, false)); - out.rows[3] = vectorSIMDf(_mm_cvtpd_ps(concat64_helper(r30, r31, _b, true))) | _mm_castps_si128((vectorSIMDf(_mm_movelh_ps(second, second)) & mask0011).getAsRegister()); - } - - return out; -} - -inline matrix4SIMD& matrix4SIMD::setScale(const core::vectorSIMDf& _scale) -{ - const __m128i mask0001 = BUILD_MASKF(0, 0, 0, 1); - - rows[0] = (_scale & BUILD_MASKF(1, 0, 0, 0)) | _mm_castps_si128((rows[0] & mask0001).getAsRegister()); - rows[1] = (_scale & BUILD_MASKF(0, 1, 0, 0)) | _mm_castps_si128((rows[1] & mask0001).getAsRegister()); - rows[2] = (_scale & BUILD_MASKF(0, 0, 1, 0)) | _mm_castps_si128((rows[2] & mask0001).getAsRegister()); - rows[3] = vectorSIMDf(0.f, 0.f, 0.f, 1.f); - - return *this; -} - -//! Returns last column of the matrix. -inline vectorSIMDf matrix4SIMD::getTranslation() const -{ - __m128 tmp1 = _mm_unpackhi_ps(rows[0].getAsRegister(), rows[1].getAsRegister()); // (0z,1z,0w,1w) - __m128 tmp2 = _mm_unpackhi_ps(rows[2].getAsRegister(), rows[3].getAsRegister()); // (2z,3z,2w,3w) - __m128 col3 = _mm_movehl_ps(tmp1, tmp2);// (0w,1w,2w,3w) - - return col3; -} -//! Returns translation part of the matrix (w component is always 0). -inline vectorSIMDf matrix4SIMD::getTranslation3D() const -{ - __m128 tmp1 = _mm_unpackhi_ps(rows[0].getAsRegister(), rows[1].getAsRegister()); // (0z,1z,0w,1w) - __m128 tmp2 = _mm_unpackhi_ps(rows[2].getAsRegister(), _mm_setzero_ps()); // (2z,0,2w,0) - __m128 transl = _mm_movehl_ps(tmp1, tmp2);// (0w,1w,2w,0) - - return transl; -} - -inline vectorSIMDf matrix4SIMD::sub3x3TransformVect(const vectorSIMDf& _in) const -{ - matrix4SIMD cp{*this}; - vectorSIMDf out = _in & BUILD_MASKF(1, 1, 1, 0); - transformVect(out); - return out; -} - -inline void matrix4SIMD::transformVect(vectorSIMDf& _out, const vectorSIMDf& _in) const -{ - vectorSIMDf r[4]; - for (size_t i = 0u; i < VectorCount; ++i) - r[i] = rows[i] * _in; - - _out = _mm_hadd_ps( - _mm_hadd_ps(r[0].getAsRegister(), r[1].getAsRegister()), - _mm_hadd_ps(r[2].getAsRegister(), r[3].getAsRegister()) - ); -} - -inline matrix4SIMD matrix4SIMD::buildProjectionMatrixPerspectiveFovRH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar) -{ - const float h = core::reciprocal(tanf(fieldOfViewRadians*0.5f)); - _NBL_DEBUG_BREAK_IF(aspectRatio == 0.f); //division by zero - const float w = h / aspectRatio; - - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix4SIMD m; - m.rows[0] = vectorSIMDf(w, 0.f, 0.f, 0.f); - m.rows[1] = vectorSIMDf(0.f, -h, 0.f, 0.f); - m.rows[2] = vectorSIMDf(0.f, 0.f, -zFar/(zFar-zNear), -zNear*zFar/(zFar-zNear)); - m.rows[3] = vectorSIMDf(0.f, 0.f, -1.f, 0.f); - - return m; -} -inline matrix4SIMD matrix4SIMD::buildProjectionMatrixPerspectiveFovLH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar) -{ - const float h = core::reciprocal(tanf(fieldOfViewRadians*0.5f)); - _NBL_DEBUG_BREAK_IF(aspectRatio == 0.f); //division by zero - const float w = h / aspectRatio; - - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix4SIMD m; - m.rows[0] = vectorSIMDf(w, 0.f, 0.f, 0.f); - m.rows[1] = vectorSIMDf(0.f, -h, 0.f, 0.f); - m.rows[2] = vectorSIMDf(0.f, 0.f, zFar/(zFar-zNear), -zNear*zFar/(zFar-zNear)); - m.rows[3] = vectorSIMDf(0.f, 0.f, 1.f, 0.f); - - return m; -} - -inline matrix4SIMD matrix4SIMD::buildProjectionMatrixOrthoRH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) -{ - _NBL_DEBUG_BREAK_IF(widthOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(heightOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix4SIMD m; - m.rows[0] = vectorSIMDf(2.f/widthOfViewVolume, 0.f, 0.f, 0.f); - m.rows[1] = vectorSIMDf(0.f, -2.f/heightOfViewVolume, 0.f, 0.f); - m.rows[2] = vectorSIMDf(0.f, 0.f, -1.f/(zFar-zNear), -zNear/(zFar-zNear)); - m.rows[3] = vectorSIMDf(0.f, 0.f, 0.f, 1.f); - - return m; -} -inline matrix4SIMD matrix4SIMD::buildProjectionMatrixOrthoLH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) -{ - _NBL_DEBUG_BREAK_IF(widthOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(heightOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix4SIMD m; - m.rows[0] = vectorSIMDf(2.f/widthOfViewVolume, 0.f, 0.f, 0.f); - m.rows[1] = vectorSIMDf(0.f, -2.f/heightOfViewVolume, 0.f, 0.f); - m.rows[2] = vectorSIMDf(0.f, 0.f, 1.f/(zFar-zNear), -zNear/(zFar-zNear)); - m.rows[3] = vectorSIMDf(0.f, 0.f, 0.f, 1.f); - - return m; -} - - - -inline __m128d matrix4SIMD::halfRowAsDouble(size_t _n, bool _firstHalf) const -{ - return _mm_cvtps_pd(_firstHalf ? rows[_n].xyxx().getAsRegister() : rows[_n].zwxx().getAsRegister()); -} -inline __m128d matrix4SIMD::concat64_helper(const __m128d& _a0, const __m128d& _a1, const matrix4SIMD& _mtx, bool _firstHalf) -{ - __m128d r0 = _mtx.halfRowAsDouble(0u, _firstHalf); - __m128d r1 = _mtx.halfRowAsDouble(1u, _firstHalf); - __m128d r2 = _mtx.halfRowAsDouble(2u, _firstHalf); - __m128d r3 = _mtx.halfRowAsDouble(3u, _firstHalf); - - //const __m128d mask01 = _mm_castsi128_pd(_mm_setr_epi32(0, 0, 0xffffffff, 0xffffffff)); - - __m128d res; - res = _mm_mul_pd(_mm_shuffle_pd(_a0, _a0, 0), r0); - res = _mm_add_pd(res, _mm_mul_pd(_mm_shuffle_pd(_a0, _a0, 3/*0b11*/), r1)); - res = _mm_add_pd(res, _mm_mul_pd(_mm_shuffle_pd(_a1, _a1, 0), r2)); - res = _mm_add_pd(res, _mm_mul_pd(_mm_shuffle_pd(_a1, _a1, 3/*0b11*/), r3)); - return res; -} - -#undef BUILD_MASKF -#undef BROADCAST32 -#else -#error "no implementation" -#endif - -inline bool matrix4SIMD::isBoxInFrustum(const aabbox3d& bbox) -{ - vectorSIMDf MinEdge, MaxEdge; - MinEdge.set(bbox.MinEdge); - MaxEdge.set(bbox.MaxEdge); - MinEdge.w = 1.f; - MaxEdge.w = 1.f; - - - auto getClosestDP = [&MinEdge,&MaxEdge](const vectorSIMDf& toDot) -> float - { - return dot(mix(MaxEdge,MinEdge,toDot struct Instance final { @@ -221,18 +221,18 @@ class ITopLevelAccelerationStructure : public IDescriptor, public IAccelerationS template struct StaticInstance final { - core::matrix3x4SIMD transform = core::matrix3x4SIMD(); + hlsl::float32_t3x4 transform = hlsl::float32_t3x4(); Instance base = {}; }; template struct MatrixMotionInstance final { - core::matrix3x4SIMD transform[2] = {core::matrix3x4SIMD(),core::matrix3x4SIMD()}; + hlsl::float32_t3x4 transform[2] = {hlsl::float32_t3x4(),hlsl::float32_t3x4()}; Instance base = {}; }; struct SRT { - // TODO: some operators to convert back and forth from `core::matrix3x4SIMD + // TODO: some operators to convert back and forth from `hlsl::float32_t3x4 float sx; float a; diff --git a/include/nbl/asset/IAnimationLibrary.h b/include/nbl/asset/IAnimationLibrary.h index 9665349103..3ab87e5d32 100644 --- a/include/nbl/asset/IAnimationLibrary.h +++ b/include/nbl/asset/IAnimationLibrary.h @@ -34,7 +34,7 @@ class IAnimationLibrary : public virtual core::IReferenceCounted translation[2] = translation[1] = translation[0] = 0.f; quat = core::vectorSIMDu32(128u,128u,128u,255u); // should be (0,0,0,1) encoded } - Keyframe(const core::vectorSIMDf& _scale, const core::quaternion& _quat, const CQuantQuaternionCache* quantCache, const core::vectorSIMDf& _translation) + Keyframe(const core::vectorSIMDf& _scale, const hlsl::math::quaternion& _quat, const CQuantQuaternionCache* quantCache, const core::vectorSIMDf& _translation) { std::copy(_translation.pointer,_translation.pointer+3,translation); quat = quantCache->template quantize(_quat); @@ -42,13 +42,13 @@ class IAnimationLibrary : public virtual core::IReferenceCounted //scale = ; } - inline core::quaternion getRotation() const + inline hlsl::math::quaternion getRotation() const { const void* _pix[4] = {&quat,nullptr,nullptr,nullptr}; double out[4]; decodePixels(_pix,out,0u,0u); auto q = core::normalize(core::vectorSIMDf(out[0],out[1],out[2],out[3])); - return reinterpret_cast(&q)[0]; + return reinterpret_cast*>(&q)[0]; } inline core::vectorSIMDf getScale() const diff --git a/include/nbl/asset/IAsset.h b/include/nbl/asset/IAsset.h index a691fa6af6..b7142713bf 100644 --- a/include/nbl/asset/IAsset.h +++ b/include/nbl/asset/IAsset.h @@ -156,24 +156,24 @@ class IAsset : virtual public core::IReferenceCounted //! inline bool isMutable() const {return m_mutable;} - inline void visitDependents(std::function visit) const - { - visitDependents_impl([&visit](const IAsset* dep)->bool - { - if (dep) - return visit(dep); - return true; - }); - } - - inline void visitDependents(std::function visit) - { - assert(isMutable()); - visitDependents([&](const IAsset* dependent) -> bool - { - return visit(const_cast(dependent)); - }); - } + inline void visitDependents(std::function visit) const + { + visitDependents_impl([&visit](const IAsset* dep)->bool + { + if (dep) + return visit(dep); + return true; + }); + } + + inline void visitDependents(std::function visit) + { + assert(isMutable()); + visitDependents([&](const IAsset* dependent) -> bool + { + return visit(const_cast(dependent)); + }); + } virtual bool valid() const = 0; @@ -194,4 +194,72 @@ concept Asset = std::is_base_of_v; } +namespace nbl::system::impl +{ +template<> +struct to_string_helper +{ + private: + using enum_t = asset::IAsset::E_TYPE; + + public: + static inline std::string __call(const enum_t value) + { + switch (value) + { + case enum_t::ET_BUFFER: + return "ICPUBuffer"; + case enum_t::ET_BUFFER_VIEW: + return "ICPUBufferView"; + case enum_t::ET_SAMPLER: + return "ICPUSampler"; + case enum_t::ET_IMAGE: + return "ICPUImage"; + case enum_t::ET_IMAGE_VIEW: + return "ICPUImageView"; + case enum_t::ET_DESCRIPTOR_SET: + return "ICPUDescriptorSet"; + case enum_t::ET_DESCRIPTOR_SET_LAYOUT: + return "ICPUDescriptorSetLayout"; + case enum_t::ET_SKELETON: + return "ICPUSkeleton"; + case enum_t::ET_ANIMATION_LIBRARY: + return "ICPUAnimationLibrary"; + case enum_t::ET_PIPELINE_LAYOUT: + return "ICPUPipelineLayout"; + case enum_t::ET_SHADER: + return "IShader"; + case enum_t::ET_GEOMETRY: + return "IGeometry"; + case enum_t::ET_RENDERPASS: + return "ICPURenderpass"; + case enum_t::ET_FRAMEBUFFER: + return "ICPUFramebuffer"; + case enum_t::ET_GRAPHICS_PIPELINE: + return "ICPUGraphicsPipeline"; + case enum_t::ET_BOTOM_LEVEL_ACCELERATION_STRUCTURE: + return "ICPUBottomLevelAccelerationStructure"; + case enum_t::ET_TOP_LEVEL_ACCELERATION_STRUCTURE: + return "ICPUTopLevelAccelerationStructure"; + case enum_t::ET_GEOMETRY_COLLECTION: + return "ICPUGeometryCollection"; + case enum_t::ET_MORPH_TARGETS: + return "ICPUMorphTargets"; + case enum_t::ET_COMPUTE_PIPELINE: + return "ICPUComputePipeline"; + case enum_t::ET_PIPELINE_CACHE: + return "ICPUPipelineCache"; + case enum_t::ET_SCENE: + return "ICPUScene"; + case enum_t::ET_RAYTRACING_PIPELINE: + return "ICPURayTracingPipeline"; + case enum_t::ET_IMPLEMENTATION_SPECIFIC_METADATA: + return ""; + default: + break; + } + return ""; + } +}; +} #endif diff --git a/include/nbl/asset/IAssetManager.h b/include/nbl/asset/IAssetManager.h index 2105b6c4fe..d9995526bc 100644 --- a/include/nbl/asset/IAssetManager.h +++ b/include/nbl/asset/IAssetManager.h @@ -385,8 +385,10 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted uint32_t getAssetLoaderCount() { return static_cast(m_loaders.vector.size()); } //! @returns 0xdeadbeefu on failure or 0-based index on success. - uint32_t addAssetLoader(core::smart_refctd_ptr&& _loader) + inline uint32_t addAssetLoader(core::smart_refctd_ptr&& _loader) { + if (!_loader) + return 0xdeadbeefu; // there's no way it ever fails, so no 0xdeadbeef return const char** exts = _loader->getAssociatedFileExtensions(); size_t extIx = 0u; @@ -395,8 +397,10 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted m_loaders.pushToVector(std::move(_loader)); return static_cast(m_loaders.vector.size())-1u; } - void removeAssetLoader(IAssetLoader* _loader) + inline void removeAssetLoader(IAssetLoader* _loader) { + if (!_loader) + return; m_loaders.eraseFromVector( std::find_if(std::begin(m_loaders.vector), std::end(m_loaders.vector), [_loader](const core::smart_refctd_ptr& a)->bool { return a.get()==_loader; }) ); @@ -409,8 +413,10 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted // Asset Writers [FOLLOWING ARE NOT THREAD SAFE] uint32_t getAssetWriterCount() { return static_cast(m_writers.perType.getSize()); } // todo.. well, it's not really writer count.. but rather type<->writer association count - void addAssetWriter(core::smart_refctd_ptr&& _writer) + inline void addAssetWriter(core::smart_refctd_ptr&& _writer) { + if (!_writer) + return; const uint64_t suppTypes = _writer->getSupportedAssetTypesBitfield(); const char** exts = _writer->getAssociatedFileExtensions(); for (uint32_t i = 0u; i < IAsset::ET_STANDARD_TYPES_COUNT; ++i) @@ -425,8 +431,10 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted } } } - void removeAssetWriter(IAssetWriter* _writer) + inline void removeAssetWriter(IAssetWriter* _writer) { + if (!_writer) + return; const uint64_t suppTypes = _writer->getSupportedAssetTypesBitfield(); const char** exts = _writer->getAssociatedFileExtensions(); size_t extIx = 0u; diff --git a/include/nbl/asset/IBuffer.h b/include/nbl/asset/IBuffer.h index 6f8c1bb35b..3a7cbb5983 100644 --- a/include/nbl/asset/IBuffer.h +++ b/include/nbl/asset/IBuffer.h @@ -120,6 +120,15 @@ struct SBufferRange inline bool operator!=(const SBufferRange& rhs) const { return !operator==(rhs); } }; +template +struct SStridedRange +{ + inline operator bool() const {return range.isValid();} + + SBufferRange range = {}; + uint32_t stride = 0; +}; + } namespace std diff --git a/include/nbl/asset/ICPUBuffer.h b/include/nbl/asset/ICPUBuffer.h index 2d10c2907b..26f45d4ced 100644 --- a/include/nbl/asset/ICPUBuffer.h +++ b/include/nbl/asset/ICPUBuffer.h @@ -119,30 +119,32 @@ class ICPUBuffer final : public asset::IBuffer, public IPreHashed return (m_alignment > 0 && !(m_alignment & (m_alignment - 1))); } -protected: - inline void discardContent_impl() override - { - if (m_data) - m_mem_resource->deallocate(m_data, m_creationParams.size, m_alignment); - m_data = nullptr; - m_mem_resource = nullptr; - m_creationParams.size = 0ull; - } - -private: - ICPUBuffer(SCreationParams&& params) : - asset::IBuffer({ params.size, EUF_TRANSFER_DST_BIT }), m_data(params.data), - m_mem_resource(params.memoryResource), m_alignment(params.alignment) {} - - ~ICPUBuffer() override { - discardContent_impl(); - } - - inline void visitDependents_impl(std::function visit) const override {} - - void* m_data; - core::smart_refctd_ptr m_mem_resource; - size_t m_alignment; + protected: + inline void discardContent_impl() override + { + if (m_data) + m_mem_resource->deallocate(m_data, m_creationParams.size, m_alignment); + m_data = nullptr; + m_mem_resource = nullptr; + m_creationParams.size = 0ull; + } + + private: + // TODO: we should remove the addition of TRANSFER_DST_BIT because its the asset converter patcher that handles that + // But we need LLVM-pipe CI first so I don't have to test 70 examples by hand + inline ICPUBuffer(SCreationParams&& params) : asset::IBuffer({params.size,params.usage|EUF_TRANSFER_DST_BIT}), + m_data(params.data), m_mem_resource(params.memoryResource), m_alignment(params.alignment) {} + + inline ~ICPUBuffer() override + { + discardContent_impl(); + } + + inline void visitDependents_impl(std::function visit) const override {} + + void* m_data; + core::smart_refctd_ptr m_mem_resource; + size_t m_alignment; }; } // end namespace nbl::asset diff --git a/include/nbl/asset/ICPUGeometryCollection.h b/include/nbl/asset/ICPUGeometryCollection.h index 6202b4de12..d231f1df00 100644 --- a/include/nbl/asset/ICPUGeometryCollection.h +++ b/include/nbl/asset/ICPUGeometryCollection.h @@ -24,11 +24,15 @@ class NBL_API2 ICPUGeometryCollection : public IAsset, public IGeometryCollectio inline E_TYPE getAssetType() const override {return AssetType;} // - inline bool valid() const //override + inline bool valid() const override { for (const auto& ref : m_geometries) - if (!ref.geometry->valid()) - return false; + { + if (!ref.operator bool() || !ref.geometry->valid()) + return false; + if (ref.jointRedirectView.src && ref.jointRedirectView.composed.getRange>().maxVx[0]>=getJointCount()) + return false; + } return true; } @@ -61,6 +65,8 @@ class NBL_API2 ICPUGeometryCollection : public IAsset, public IGeometryCollectio return false; } + // + inline const core::vector& getGeometries() const {return base_t::getGeometries();} // inline core::vector* getGeometries() { diff --git a/include/nbl/asset/ICPUMorphTargets.h b/include/nbl/asset/ICPUMorphTargets.h index 545d2cd8a9..29924f9727 100644 --- a/include/nbl/asset/ICPUMorphTargets.h +++ b/include/nbl/asset/ICPUMorphTargets.h @@ -23,7 +23,7 @@ class NBL_API2 ICPUMorphTargets : public IAsset, public IMorphTargetsvalid()) @@ -55,7 +55,7 @@ class NBL_API2 ICPUMorphTargets : public IAsset, public IMorphTargets visit) const //override + inline void visitDependents_impl(std::function visit) const override { auto nonNullOnly = [&visit](const IAsset* dep)->bool { diff --git a/include/nbl/asset/ICPURayTracingPipeline.h b/include/nbl/asset/ICPURayTracingPipeline.h index 01cf4a3f28..e882b4b4cb 100644 --- a/include/nbl/asset/ICPURayTracingPipeline.h +++ b/include/nbl/asset/ICPURayTracingPipeline.h @@ -13,9 +13,9 @@ namespace nbl::asset { //! CPU Version of RayTracing Pipeline -class ICPURayTracingPipeline final : public ICPUPipeline> +class ICPURayTracingPipeline final : public ICPUPipeline> { - using pipeline_base_t = IRayTracingPipeline; + using pipeline_base_t = IRayTracingPipeline; using base_t = ICPUPipeline; public: diff --git a/include/nbl/asset/ICPUScene.h b/include/nbl/asset/ICPUScene.h new file mode 100644 index 0000000000..56a069c469 --- /dev/null +++ b/include/nbl/asset/ICPUScene.h @@ -0,0 +1,242 @@ +// 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_SCENE_H_INCLUDED_ +#define _NBL_ASSET_I_CPU_SCENE_H_INCLUDED_ + + +#include "nbl/core/containers/CMemoryPool.h" + +#include "nbl/asset/IScene.h" +#include "nbl/asset/ICPUMorphTargets.h" +#include "nbl/asset/material_compiler3/CTrueIR.h" + + +namespace nbl::asset +{ +// +class ICPUScene final : public IAsset, public IScene +{ + using base_t = IScene; + using material_table_allocator_t = core::GeneralpurposeAddressAllocatorST; +// using material_table_t = core::CMemoryPool<,core::allocator,false>; + + public: + using material_pool_t = material_compiler3::CTrueIR; + // + static inline core::smart_refctd_ptr create(core::smart_refctd_ptr&& ir, const uint8_t maxMorphTargetGeometryCountLog2=16) + { + return core::smart_refctd_ptr(new ICPUScene(std::move(ir),maxMorphTargetGeometryCountLog2),core::dont_grab); + } + + constexpr static inline auto AssetType = ET_SCENE; + inline E_TYPE getAssetType() const override { return AssetType; } + + inline bool valid() const override + { + if (!m_instances) + return false; + auto materialTableOffsetIt = m_instances.materials.begin(); + for (const auto& targets : m_instances.morphTargets) + { + const auto materialTableOffset = *(materialTableOffsetIt++); + // TODO: check if `materialTableOffset` can be contained in `materialTable` + if (!targets || targets->valid()) + return false; + const auto geoCount = targets->getGeometryExclusiveCount({}); + // TODO: check if `materialTableOffset+geoCount` can be contained in `materialTable` + // TODO: check every material is either null or belongs in `m_materialPool` + } + return true; + } + + inline core::smart_refctd_ptr clone(uint32_t _depth=~0u) const + { + const auto nextDepth = _depth ? (_depth-1):0; + // TODO: copy the material_table state/contents! + // the True IR isn't an asset (yet), but it probably should be? + auto retval = create(core::smart_refctd_ptr(m_materialPool),m_maxMorphTargetGeometryCountLog2); + if (nextDepth) + { + retval->m_instances.morphTargets.resize(retval->m_instances.size()); + for (auto& targets : m_instances.morphTargets) + retval->m_instances.morphTargets.push_back(core::move_and_static_cast(targets->clone(nextDepth))); + retval->m_envLightTexs.reserve(m_envLightTexs.size()); + for (const auto& tex : m_envLightTexs) + retval->m_envLightTexs.push_back(core::move_and_static_cast(tex->clone(nextDepth))); + } + else + { + retval->m_instances = m_instances; + retval->m_envLightTexs = m_envLightTexs; + } + retval->m_instances.materials = m_instances.materials; + retval->m_instances.initialTransforms = m_instances.initialTransforms; + retval->m_envLightTypes = m_envLightTypes; + return retval; + } + + // TODO: change to CRootNode + using material_t = material_pool_t::TypedHandle; + inline material_compiler3::CTrueIR* getMaterialPool() {return m_materialPool.get();} + inline const material_compiler3::CTrueIR* getMaterialPool() const {return m_materialPool.get();} + + // + using material_table_offset_t = uint32_t; + constexpr static inline material_table_offset_t InvalidMaterialTable = ~0u; + material_table_offset_t allocateMaterialTable(const ICPUMorphTargets* targets) + { + if (!targets) + return material_table_allocator_t::invalid_address; + return allocateMaterialTable(targets->getGeometryExclusiveCount({})); + } + material_table_offset_t allocateMaterialTable(const uint32_t count) + { + // TODO: implement + return material_table_allocator_t::invalid_address; + } + void deallocateMaterialTable(const material_table_offset_t offset, const ICPUMorphTargets* targets) + { + return deallocateMaterialTable(offset,targets->getGeometryExclusiveCount({})); + } + void deallocateMaterialTable(const material_table_offset_t offset, const uint32_t count) + { + // TODO: implement + } + + // TODO: get material table pointer + + // TODO: wrap up in some ECS storage class + struct SInstanceStorage final + { + public: + inline SInstanceStorage(const size_t size=1) : morphTargets(size), materials(size), initialTransforms(size) {} + + inline void clearInitialTransforms() {initialTransforms.clear();} + + inline operator bool() const + { + if (morphTargets.size()!=materials.size()) + return false; + if (initialTransforms.empty()) + return true; + return morphTargets.size()==initialTransforms.size(); + } + + inline void reserve(const size_t newSize) + { + morphTargets.reserve(newSize); + materials.reserve(newSize); + if (!initialTransforms.empty()) + initialTransforms.reserve(newSize); + } + + inline void resize(const size_t newSize) + { + morphTargets.resize(newSize); + materials.resize(newSize,InvalidMaterialTable); + if (!initialTransforms.empty()) + initialTransforms.resize(newSize,ICPUGeometryCollection::SGeometryReference{}.transform); + } + + inline void erase(const size_t first, const size_t last) + { + morphTargets.erase(morphTargets.begin()+first,morphTargets.begin()+last); + materials.erase(materials.begin()+first, materials.begin()+last); + initialTransforms.erase(initialTransforms.begin()+first,initialTransforms.begin()+last); + } + inline void erase(const size_t ix) {return erase(ix,ix+1);} + + inline size_t size() const {return morphTargets.size();} + + inline std::span> getMorphTargets() {return morphTargets;} + inline std::span> getMorphTargets() const {return morphTargets;} + + inline std::span getMaterialTables() {return materials;} + inline std::span getMaterialTables() const {return materials;} + + inline std::span getInitialTransforms() {return initialTransforms;} + inline std::span getInitialTransforms() const {return initialTransforms;} + + private: + friend class ICPUScene; + + core::vector> morphTargets; + // One material table per morph target, + // Within each morph target, one material per geometry + core::vector materials; + core::vector initialTransforms; + // TODO: animations (keyframed transforms, skeleton instance) + }; + + // + inline SInstanceStorage& getInstances() {return m_instances;} + inline const SInstanceStorage& getInstances() const {return m_instances;} + + enum class EEnvLightType : uint8_t + { + Cubemap, + SphereMap, // u=theta, v=phi with (0,0) being top right of image + OctahedralMap, + Count + }; + // + inline bool addEnvLight(const EEnvLightType type, core::smart_refctd_ptr&& tex) + { + if (!tex) + return false; + using view_e = IImageViewBase::E_TYPE; + switch (tex->getCreationParameters().viewType) + { + case view_e::ET_2D: [[fallthrough]]; + case view_e::ET_2D_ARRAY: + m_envLightTypes.push_back(type); + break; + case view_e::ET_CUBE_MAP: [[fallthrough]]; + case view_e::ET_CUBE_MAP_ARRAY: + if (type!=EEnvLightType::Cubemap) + return false; + m_envLightTypes.push_back(EEnvLightType::Cubemap); + break; + default: + return false; + } + m_envLightTexs.push_back(std::move(tex)); + return true; + } + // + inline std::span getEnviornmentLightTypes() const {return m_envLightTypes;} + inline std::span getEnvironmentLightTextures() const {return {&m_envLightTexs.data()->get(),m_envLightTexs.size()}; } + // TODO: add an erase_if and erase with begin/end iterators + inline void clearEnvLights() + { + m_envLightTexs.clear(); + m_envLightTypes.clear(); + } + + // + hlsl::float32_t3 m_ambientLight; + + protected: + inline ICPUScene(core::smart_refctd_ptr&& materialPool, const uint32_t maxMorphTargetGeometryCountLog2) : + m_materialPool(std::move(materialPool)), m_maxMorphTargetGeometryCountLog2(maxMorphTargetGeometryCountLog2) {} + // + inline void visitDependents_impl(std::function visit) const override + { + assert(false && "Unimplemented"); // we'd probalby be going over the: morph targets, image views, ... + } + + // +// TODO material_table_t m_materialTable; + core::smart_refctd_ptr m_materialPool; + // + SInstanceStorage m_instances; + // + core::vector> m_envLightTexs; + core::vector m_envLightTypes; + // + const uint8_t m_maxMorphTargetGeometryCountLog2; +}; +} + +#endif \ No newline at end of file diff --git a/include/nbl/asset/ICPUSkeleton.h b/include/nbl/asset/ICPUSkeleton.h index 1049798268..7418e46ce3 100644 --- a/include/nbl/asset/ICPUSkeleton.h +++ b/include/nbl/asset/ICPUSkeleton.h @@ -42,15 +42,15 @@ class ICPUSkeleton final : public ISkeleton, public IAsset } //! - inline const core::matrix3x4SIMD& getDefaultTransformMatrix(base_t::joint_id_t jointID) const + inline const hlsl::float32_t3x4& getDefaultTransformMatrix(base_t::joint_id_t jointID) const { const uint8_t* ptr = reinterpret_cast(m_defaultTransforms.buffer->getPointer()); - return reinterpret_cast(ptr+m_defaultTransforms.offset)[jointID]; + return reinterpret_cast(ptr+m_defaultTransforms.offset)[jointID]; } - inline core::matrix3x4SIMD& getDefaultTransformMatrix(base_t::joint_id_t jointID) + inline hlsl::float32_t3x4& getDefaultTransformMatrix(base_t::joint_id_t jointID) { assert(isMutable()); - return const_cast(const_cast(this)->getDefaultTransformMatrix(jointID)); + return const_cast(const_cast(this)->getDefaultTransformMatrix(jointID)); } //! diff --git a/include/nbl/asset/IGeometryCollection.h b/include/nbl/asset/IGeometryCollection.h index 29c145c115..1fac15ec17 100644 --- a/include/nbl/asset/IGeometryCollection.h +++ b/include/nbl/asset/IGeometryCollection.h @@ -21,7 +21,7 @@ class NBL_API2 IGeometryCollection : public virtual core::IReferenceCounted inline const auto& getAABB() const {return m_aabb;} // - struct SGeometryReference + struct SGeometryReference final { inline operator bool() const { @@ -29,9 +29,9 @@ class NBL_API2 IGeometryCollection : public virtual core::IReferenceCounted return false; if (jointRedirectView.src) { - if (!jointRedirectView.isFormattedScalarInteger()) + if (!jointRedirectView.composed.isFormattedScalarInteger()) return false; - if (jointRedirectView.getElementCount()getJointCount()*2) + if (jointRedirectView.getElementCount()getJointCount()) return false; } else diff --git a/include/nbl/asset/IMorphTargets.h b/include/nbl/asset/IMorphTargets.h index 14265aa71a..6f208c6f73 100644 --- a/include/nbl/asset/IMorphTargets.h +++ b/include/nbl/asset/IMorphTargets.h @@ -19,6 +19,7 @@ class NBL_API2 IMorphTargets : public virtual core::IReferenceCounted public: struct index_t { + inline index_t() = default; explicit inline index_t(uint32_t _value) : value(_value) {} inline operator bool() const {return value!=(~0u);} @@ -30,6 +31,19 @@ class NBL_API2 IMorphTargets : public virtual core::IReferenceCounted { return static_cast(m_targets.size()); } + // + virtual inline uint32_t getGeometryExclusiveCount(index_t index) const + { + if (const auto targetCount=getTargetCount(); index.value>targetCount) + index.value = targetCount; + uint32_t retval = 0; + for (uint32_t i=0; igetGeometries().size(); + } + return retval; + } template requires std::is_floating_point_v struct SInterpolants @@ -56,7 +70,11 @@ class NBL_API2 IMorphTargets : public virtual core::IReferenceCounted { inline operator bool() const { - return geoCollection && (!jointRedirectView || jointRedirectView.composed.isFormattedScalarInteger()); + if (!geoCollection) + return false; + if (!jointRedirectView) + return true; + return jointRedirectView.composed.isFormattedScalarInteger() && jointRedirectView.getElementCount()>=geoCollection->getJointCount(); } core::smart_refctd_ptr geoCollection = {}; diff --git a/include/nbl/asset/IPipelineLayout.h b/include/nbl/asset/IPipelineLayout.h index 430c812dcb..0244aaaa06 100644 --- a/include/nbl/asset/IPipelineLayout.h +++ b/include/nbl/asset/IPipelineLayout.h @@ -89,6 +89,7 @@ template class IPipelineLayout { public: + using desc_layout_t = DescLayoutType; static inline constexpr uint32_t DESCRIPTOR_SET_COUNT = 4u; std::span getDescriptorSetLayouts() const diff --git a/include/nbl/asset/IRayTracingPipeline.h b/include/nbl/asset/IRayTracingPipeline.h index fe318d271a..23d2fac81f 100644 --- a/include/nbl/asset/IRayTracingPipeline.h +++ b/include/nbl/asset/IRayTracingPipeline.h @@ -13,49 +13,102 @@ namespace nbl::asset class IRayTracingPipelineBase : public virtual core::IReferenceCounted { - public: - #define base_flag(F) static_cast(IPipelineBase::FLAGS::F) - enum class CreationFlags : uint64_t - { - NONE = base_flag(NONE), - // there's a bit of a problem, as the ICPUCompute and Graphics pipelines don't care about flags, because the following 4 flags - DISABLE_OPTIMIZATIONS = base_flag(DISABLE_OPTIMIZATIONS), - ALLOW_DERIVATIVES = base_flag(ALLOW_DERIVATIVES), - FAIL_ON_PIPELINE_COMPILE_REQUIRED = base_flag(FAIL_ON_PIPELINE_COMPILE_REQUIRED), - EARLY_RETURN_ON_FAILURE = base_flag(EARLY_RETURN_ON_FAILURE), - // don't matter for ICPU Pipelines, we'd really need to have these separate from `base_flag` and use the `IRayTracingPipelineBase::CreationFlags` for the ICPU creation params only - SKIP_BUILT_IN_PRIMITIVES = 1<<12, - SKIP_AABBS = 1<<13, - NO_NULL_ANY_HIT_SHADERS = 1<<14, - NO_NULL_CLOSEST_HIT_SHADERS = 1<<15, - NO_NULL_MISS_SHADERS = 1<<16, - NO_NULL_INTERSECTION_SHADERS = 1<<17, - ALLOW_MOTION = 1<<20, - }; - #undef base_flag - - struct SCachedCreationParams final - { - core::bitflag flags = CreationFlags::NONE; - uint32_t maxRecursionDepth : 6 = 0; - uint32_t dynamicStackSize : 1 = false; - }; + public: + #define base_flag(F) static_cast(IPipelineBase::FLAGS::F) + enum class CreationFlags : uint64_t + { + NONE = base_flag(NONE), + // there's a bit of a problem, as the ICPUCompute and Graphics pipelines don't care about flags, because the following 4 flags + DISABLE_OPTIMIZATIONS = base_flag(DISABLE_OPTIMIZATIONS), + ALLOW_DERIVATIVES = base_flag(ALLOW_DERIVATIVES), + FAIL_ON_PIPELINE_COMPILE_REQUIRED = base_flag(FAIL_ON_PIPELINE_COMPILE_REQUIRED), + EARLY_RETURN_ON_FAILURE = base_flag(EARLY_RETURN_ON_FAILURE), + // don't matter for ICPU Pipelines, we'd really need to have these separate from `base_flag` and use the `IRayTracingPipelineBase::CreationFlags` for the ICPU creation params only + SKIP_BUILT_IN_PRIMITIVES = 1<<12, + SKIP_AABBS = 1<<13, + NO_NULL_ANY_HIT_SHADERS = 1<<14, + NO_NULL_CLOSEST_HIT_SHADERS = 1<<15, + NO_NULL_MISS_SHADERS = 1<<16, + NO_NULL_INTERSECTION_SHADERS = 1<<17, + ALLOW_MOTION = 1<<20, + }; + #undef base_flag + + struct SCachedCreationParams final + { + core::bitflag flags = CreationFlags::NONE; + uint32_t maxRecursionDepth : 6 = 0; + uint32_t dynamicStackSize : 1 = false; + }; }; -template +template class IRayTracingPipeline : public IPipeline, public IRayTracingPipelineBase { - public: + public: + struct SShaderBindingTable + { + inline bool valid(const core::bitflag flags) const + { + return valid(flags,[](const std::string_view, auto... args)->void{}); + } + template + inline bool valid(const core::bitflag flags, Callback&& cb) const + { + using create_flag_e = IRayTracingPipelineBase::CreationFlags; + // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03696 + // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03697 + // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03512 + const auto shouldHaveHitGroup = flags & (core::bitflag(create_flag_e::NO_NULL_ANY_HIT_SHADERS) | create_flag_e::NO_NULL_CLOSEST_HIT_SHADERS | create_flag_e::NO_NULL_INTERSECTION_SHADERS); + if (shouldHaveHitGroup && !hit.range.buffer) + { + cb("bound pipeline indicates that traceRays command should have hit group, but SRayTracingSBT::hit::range::buffer is null!"); + return false; + } + + // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03511 + const auto shouldHaveMissGroup = flags & create_flag_e::NO_NULL_MISS_SHADERS; + if (shouldHaveMissGroup && !miss.range.buffer) + { + cb("bound pipeline indicates that traceRays command should have miss group, but SRayTracingSBT::hit::range::buffer is null!"); + return false; + } + + auto invalidBufferRegion = [&cb](const SStridedRange& stRange, const char* groupName) -> bool + { + const auto& range = stRange.range; + const auto* const buffer = range.buffer.get(); + if (!buffer) + return false; + + if (!range.isValid()) + { + cb("%s buffer range is not valid!",groupName); + return false; + } + + return false; + }; + + if (invalidBufferRegion({.range=raygen},"Raygen Group")) return false; + if (invalidBufferRegion(miss,"Miss groups")) return false; + if (invalidBufferRegion(hit,"Hit groups")) return false; + if (invalidBufferRegion(callable,"Callable groups")) return false; + + return true; + } + + asset::SBufferRange raygen = {}; + asset::SStridedRange miss = {}, hit = {}, callable = {}; + }; - inline const SCachedCreationParams& getCachedCreationParams() const { return m_params; } + inline const SCachedCreationParams& getCachedCreationParams() const { return m_params; } - protected: - explicit IRayTracingPipeline(PipelineLayoutType* layout, const SCachedCreationParams& cachedParams) : - IPipeline(core::smart_refctd_ptr(layout)), - m_params(cachedParams) - {} + protected: + explicit inline IRayTracingPipeline(PipelineLayoutType* layout, const SCachedCreationParams& cachedParams) : + IPipeline(core::smart_refctd_ptr(layout)), m_params(cachedParams) {} - SCachedCreationParams m_params; + SCachedCreationParams m_params; }; diff --git a/include/nbl/asset/IScene.h b/include/nbl/asset/IScene.h new file mode 100644 index 0000000000..69bf00ab3a --- /dev/null +++ b/include/nbl/asset/IScene.h @@ -0,0 +1,23 @@ +// 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_SCENE_H_INCLUDED_ +#define _NBL_ASSET_I_SCENE_H_INCLUDED_ + + +#include "nbl/asset/IMorphTargets.h" + + +namespace nbl::asset +{ +// This is incredibly temporary, lots of things are going to change +class NBL_API2 IScene : public virtual core::IReferenceCounted +{ + public: + + protected: + virtual ~IScene() = default; +}; +} + +#endif \ No newline at end of file diff --git a/include/nbl/asset/ISkeleton.h b/include/nbl/asset/ISkeleton.h index 7960ca4eef..03ba3af4ea 100644 --- a/include/nbl/asset/ISkeleton.h +++ b/include/nbl/asset/ISkeleton.h @@ -62,7 +62,7 @@ class ISkeleton : public virtual core::IReferenceCounted return; assert(m_parentJointIDs.buffer->getSize()>=m_parentJointIDs.offset+sizeof(joint_id_t)*m_jointCount); - assert(m_defaultTransforms.buffer->getSize()>=m_defaultTransforms.offset+sizeof(core::matrix3x4SIMD)*m_jointCount); + assert(m_defaultTransforms.buffer->getSize()>=m_defaultTransforms.offset+sizeof(hlsl::float32_t3x4)*m_jointCount); } virtual ~ISkeleton() { diff --git a/include/nbl/asset/asset.h b/include/nbl/asset/asset.h index fe70e81646..3d21842195 100644 --- a/include/nbl/asset/asset.h +++ b/include/nbl/asset/asset.h @@ -61,12 +61,13 @@ #include "nbl/asset/interchange/IAssetLoader.h" #include "nbl/asset/interchange/IImageLoader.h" #include "nbl/asset/interchange/IGeometryLoader.h" +#include "nbl/asset/interchange/ISceneLoader.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/CPLYMetadata.h" #include "nbl/asset/metadata/CSTLMetadata.h" -//#include "nbl/asset/metadata/CIESProfileMetadata.h" +#include "nbl/asset/metadata/CIESProfileMetadata.h" #endif diff --git a/include/nbl/asset/interchange/IAssetLoader.h b/include/nbl/asset/interchange/IAssetLoader.h index 64ed4a7fd3..a194f0e13e 100644 --- a/include/nbl/asset/interchange/IAssetLoader.h +++ b/include/nbl/asset/interchange/IAssetLoader.h @@ -4,6 +4,7 @@ #ifndef _NBL_ASSET_I_ASSET_LOADER_H_INCLUDED_ #define _NBL_ASSET_I_ASSET_LOADER_H_INCLUDED_ + #include "nbl/system/declarations.h" #include "nbl/system/ISystem.h" @@ -11,6 +12,7 @@ #include "nbl/asset/interchange/SAssetBundle.h" + namespace nbl::asset { @@ -85,24 +87,24 @@ class NBL_API2 IAssetLoader : public virtual core::IReferenceCounted enum E_LOADER_PARAMETER_FLAGS : uint64_t { - ELPF_NONE = 0, //!< default value, it doesn't do anything - ELPF_RIGHT_HANDED_MESHES = 0x1, //!< specifies that a mesh will be flipped in such a way that it'll look correctly in right-handed camera system - ELPF_DONT_COMPILE_GLSL = 0x2, //!< it states that GLSL won't be compiled to SPIR-V if it is loaded or generated - ELPF_LOAD_METADATA_ONLY = 0x4 //!< it forces the loader to not load the entire scene for performance in special cases to fetch metadata. + ELPF_NONE = 0, //!< default value, it doesn't do anything +//[[deprecated]] ELPF_RIGHT_HANDED_MESHES = 0x1, //!< specifies that a mesh will be flipped in such a way that it'll look correctly in right-handed camera system +//[[deprecated]] ELPF_DONT_COMPILE_GLSL = 0x2, //!< it states that GLSL won't be compiled to SPIR-V if it is loaded or generated + ELPF_LOAD_METADATA_ONLY = 0x4 //!< it forces the loader to not load the entire scene for performance in special cases to fetch metadata. }; struct SAssetLoadParams { - SAssetLoadParams(size_t _decryptionKeyLen = 0u, const uint8_t* _decryptionKey = nullptr, - E_CACHING_FLAGS _cacheFlags = ECF_CACHE_EVERYTHING,const E_LOADER_PARAMETER_FLAGS& _loaderFlags = ELPF_NONE, - system::logger_opt_ptr _logger = nullptr, const std::filesystem::path& cwd = "") : + inline SAssetLoadParams(const size_t _decryptionKeyLen = 0u, const uint8_t* const _decryptionKey = nullptr, + const E_CACHING_FLAGS _cacheFlags = ECF_CACHE_EVERYTHING,const E_LOADER_PARAMETER_FLAGS _loaderFlags = ELPF_NONE, + const system::logger_opt_ptr _logger = nullptr, const std::filesystem::path& cwd = "") : decryptionKeyLen(_decryptionKeyLen), decryptionKey(_decryptionKey), cacheFlags(_cacheFlags), loaderFlags(_loaderFlags), logger(std::move(_logger)), workingDirectory(cwd) { } - SAssetLoadParams(const SAssetLoadParams& rhs, bool _reload = false) : + inline SAssetLoadParams(const SAssetLoadParams& rhs, const bool _reload=false) : decryptionKeyLen(rhs.decryptionKeyLen), decryptionKey(rhs.decryptionKey), cacheFlags(rhs.cacheFlags), @@ -175,6 +177,9 @@ class NBL_API2 IAssetLoader : public virtual core::IReferenceCounted public: NBL_API2 IAssetLoaderOverride(IAssetManager* _manager); + // + inline IAssetManager* getManager() const {return m_manager;} + //! template inline std::pair,const IAssetMetadata*> findDefaultAsset(const std::string& inSearchKey, const SAssetLoadContext& ctx, const uint32_t hierarchyLevel) @@ -268,7 +273,7 @@ class NBL_API2 IAssetLoader : public virtual core::IReferenceCounted //! After a successful load of an asset or sub-asset //TODO change name - virtual void insertAssetIntoCache(SAssetBundle& asset, const std::string& supposedKey, const SAssetLoadContext& ctx, const uint32_t hierarchyLevel); + virtual void insertAssetIntoCache(SAssetBundle& asset, const std::string& supposedKey, const SAssetLoadParams& _params, const uint32_t hierarchyLevel); }; public: @@ -289,14 +294,18 @@ class NBL_API2 IAssetLoader : public virtual core::IReferenceCounted //! Loads an asset from an opened file, returns nullptr in case of failure. virtual SAssetBundle loadAsset(system::IFile* _file, const SAssetLoadParams& _params, IAssetLoaderOverride* _override, uint32_t _hierarchyLevel = 0u) = 0; + // virtual void initialize() {} + // + static core::smart_refctd_ptr createDefaultImageView(core::smart_refctd_ptr&& image); + protected: // accessors for loaders - SAssetBundle interm_getAssetInHierarchy(IAssetManager* _mgr, system::IFile* _file, const std::string& _supposedFilename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override); - SAssetBundle interm_getAssetInHierarchy(IAssetManager* _mgr, const std::string& _filename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override); + SAssetBundle interm_getAssetInHierarchy(system::IFile* _file, const std::string& _supposedFilename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override); + SAssetBundle interm_getAssetInHierarchy(const std::string& _filename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override); // only the overload we use for now - SAssetBundle interm_getAssetInHierarchyWithAllContent(IAssetManager* _mgr, const std::string& _filename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override); + SAssetBundle interm_getAssetInHierarchyWithAllContent(const std::string& _filename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override); void interm_setAssetMutability(const IAssetManager* _mgr, IAsset* _asset, const bool _val); @@ -304,10 +313,86 @@ class NBL_API2 IAssetLoader : public virtual core::IReferenceCounted bool insertBuiltinAssetIntoCache(IAssetManager* _mgr, core::smart_refctd_ptr& _asset, core::smart_refctd_ptr&& metadata, const std::string _path); bool insertBuiltinAssetIntoCache(IAssetManager* _mgr, core::smart_refctd_ptr&& _asset, core::smart_refctd_ptr&& metadata, const std::string _path); + // TODO: make static? inline void setAssetInBundle(SAssetBundle& bundle, const uint32_t offset, core::smart_refctd_ptr&& _asset) { bundle.setAsset(offset,std::move(_asset)); } + + // + template requires is_any_of_v + SAssetBundle interm_getImageViewInHierarchy(const PathOrFile& pathOrFile, const IAssetLoader::SAssetLoadParams& _params, const uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) + { + // TODO: first we needed to try-load to figure out the cache key to be used, but we could do it differently I guess + // TODO: this load shouldn't add to cache until we exit successfully + auto bundle = interm_getAssetInHierarchy(pathOrFile,_params,_hierarchyLevel,_override); + auto contentRange = bundle.getContents(); + if (contentRange.empty()) + return {}; + if (const auto origType=bundle.getAssetType(); origType==IAsset::E_TYPE::ET_IMAGE_VIEW) + return bundle; + else if (origType!=IAsset::E_TYPE::ET_IMAGE) + { + _params.logger.log( + "IAssetLoader::interm_getImageViewInHierarchy loaded assed with key \"%s\" with was of type %s not IMAGE", + system::ILogger::ELL_ERROR,bundle.getCacheKey().c_str(),system::to_string(origType).c_str() + ); + return {}; + } + + const auto cacheKey = bundle.getCacheKey()+"?view?IAssetLoader?default"; + SAssetLoadContext ctx(_params,nullptr); + // search the cache for the imageview + { + const asset::IAsset::E_TYPE types[]{asset::IAsset::ET_IMAGE_VIEW,asset::IAsset::ET_TERMINATING_ZERO}; + auto cachedBundle = _override->findCachedAsset(cacheKey,types,ctx,_hierarchyLevel); + // check if found + if (!cachedBundle.getContents().empty()) + return cachedBundle; + } + + // ok now create default views for all the images + auto container = core::make_refctd_dynamic_array(contentRange.size()); + auto outIt = container->begin(); + for (auto& asset : contentRange) + *(outIt++) = createDefaultImageView(core::smart_refctd_ptr_static_cast(asset)); + bundle = SAssetBundle(nullptr,std::move(container)); + _override->insertAssetIntoCache(bundle,cacheKey,_params,_hierarchyLevel); + return bundle; + } + +#if 0 + // should we have derivative, bump and normalmap getters or shall we support all 3 in Frontend? + static std::string imageViewCacheKey(const CElementTexture::Bitmap& bitmap, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic) + { + std::string key = bitmap.filename.svalue; + switch (semantic) + { + case CMitsubaMaterialCompilerFrontend::EIVS_NORMAL_MAP: + key += "?deriv?n"; + break; + case CMitsubaMaterialCompilerFrontend::EIVS_BUMP_MAP: + key += "?deriv?h"; + { + static const char* wrap[5] + { + "?repeat", + "?mirror", + "?clamp", + "?zero", + "?one" + }; + key += wrap[bitmap.wrapModeU]; + key += wrap[bitmap.wrapModeV]; + } + break; + default: + break; + } + key += "?view"; + return key; + } +#endif }; } diff --git a/include/nbl/asset/interchange/IImageLoader.h b/include/nbl/asset/interchange/IImageLoader.h index c7b6119ede..a1177e7d00 100644 --- a/include/nbl/asset/interchange/IImageLoader.h +++ b/include/nbl/asset/interchange/IImageLoader.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_I_IMAGE_LOADER_H_INCLUDED__ -#define __NBL_ASSET_I_IMAGE_LOADER_H_INCLUDED__ +#ifndef _NBL_ASSET_I_IMAGE_LOADER_H_INCLUDED_ +#define _NBL_ASSET_I_IMAGE_LOADER_H_INCLUDED_ #include "nbl/core/declarations.h" diff --git a/include/nbl/asset/interchange/ISceneLoader.h b/include/nbl/asset/interchange/ISceneLoader.h new file mode 100644 index 0000000000..f61a2d3cea --- /dev/null +++ b/include/nbl/asset/interchange/ISceneLoader.h @@ -0,0 +1,30 @@ +// 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_SCENE_LOADER_H_INCLUDED_ +#define _NBL_ASSET_I_SCENE_LOADER_H_INCLUDED_ + + +#include "nbl/core/declarations.h" + +#include "nbl/asset/ICPUScene.h" +#include "nbl/asset/interchange/IAssetLoader.h" + + +namespace nbl::asset +{ + +class ISceneLoader : public IAssetLoader +{ + public: + virtual inline uint64_t getSupportedAssetTypesBitfield() const override {return IAsset::ET_SCENE;} + + protected: + inline ISceneLoader() {} + + private: +}; + +} + +#endif diff --git a/include/nbl/asset/material_compiler3/CTrueIR.h b/include/nbl/asset/material_compiler3/CTrueIR.h index a6937f00ab..3d5c89ff58 100644 --- a/include/nbl/asset/material_compiler3/CTrueIR.h +++ b/include/nbl/asset/material_compiler3/CTrueIR.h @@ -17,7 +17,7 @@ class CTrueIR : public CNodePool { public: // constructor - inline core::smart_refctd_ptr create(const uint8_t chunkSizeLog2=19, const uint8_t maxNodeAlignLog2=4, refctd_pmr_t&& _pmr={}) + static inline core::smart_refctd_ptr create(const uint8_t chunkSizeLog2=19, const uint8_t maxNodeAlignLog2=4, refctd_pmr_t&& _pmr={}) { if (chunkSizeLog2<14 || maxNodeAlignLog2<4) return nullptr; diff --git a/include/nbl/asset/metadata/IAssetMetadata.h b/include/nbl/asset/metadata/IAssetMetadata.h index 584fe474f8..d574091dd2 100644 --- a/include/nbl/asset/metadata/IAssetMetadata.h +++ b/include/nbl/asset/metadata/IAssetMetadata.h @@ -10,6 +10,7 @@ #include "nbl/asset/metadata/IImageMetadata.h" #include "nbl/asset/metadata/IImageViewMetadata.h" #include "nbl/asset/metadata/IPolygonGeometryMetadata.h" +#include "nbl/asset/metadata/IGeometryCollectionMetadata.h" namespace nbl::asset @@ -38,6 +39,11 @@ struct IAssetMetadata_base::asset_metadata { using type = IPolygonGeometryMetadata; }; +template<> +struct IAssetMetadata_base::asset_metadata +{ + using type = IGeometryCollectionMetadata; +}; } @@ -79,7 +85,8 @@ 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/IGeometryCollectionMetadata.h b/include/nbl/asset/metadata/IGeometryCollectionMetadata.h new file mode 100644 index 0000000000..02ace03f82 --- /dev/null +++ b/include/nbl/asset/metadata/IGeometryCollectionMetadata.h @@ -0,0 +1,30 @@ +// 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_METADATA_H_INCLUDED_ +#define _NBL_ASSET_I_GEOMETRY_COLLECTION_METADATA_H_INCLUDED_ + + +#include "nbl/asset/ICPUGeometryCollection.h" + + +namespace nbl::asset +{ + +//! +class IGeometryCollectionMetadata : public core::Interface +{ + public: + inline IGeometryCollectionMetadata() = default; + + protected: + virtual ~IGeometryCollectionMetadata() = default; + + inline IGeometryCollectionMetadata& operator=(IGeometryCollectionMetadata&& other) + { + return *this; + } +}; + +} +#endif diff --git a/include/nbl/asset/utils/CGeometryCreator.h b/include/nbl/asset/utils/CGeometryCreator.h index 5c669b9510..290c81b239 100644 --- a/include/nbl/asset/utils/CGeometryCreator.h +++ b/include/nbl/asset/utils/CGeometryCreator.h @@ -115,9 +115,15 @@ class NBL_API2 CGeometryCreator final : public core::IReferenceCounted \param subdivision Specifies subdivision level of the icosphere. \param smooth Specifies whether vertecies should be built for smooth or flat shading. */ - core::smart_refctd_ptr createIcoSphere(float radius=1.f, uint32_t subdivision=1, bool smooth=false) const; + //! Create a grid geometry + /** + No vertex buffer, only index in triangle strip topology without reset, "snake" with degenerates + \param "resolution" Specifies resolution of grid + */ + core::smart_refctd_ptr createGrid(const hlsl::uint16_t2 resolution = { 128u, 128u }) const; + private: SCreationParams m_params; }; diff --git a/include/nbl/asset/utils/COBBGenerator.h b/include/nbl/asset/utils/COBBGenerator.h new file mode 100644 index 0000000000..cf2afcecaa --- /dev/null +++ b/include/nbl/asset/utils/COBBGenerator.h @@ -0,0 +1,532 @@ + +// 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_C_OBB_GENERATOR_H_INCLUDED_ +#define _NBL_ASSET_C_OBB_GENERATOR_H_INCLUDED_ + +#include "nbl/builtin/hlsl/shapes/obb.hlsl" + +namespace nbl::asset +{ + +class COBBGenerator +{ + private: + template + struct Extremals + { + std::array values; + + T* minPtr() + { + return values.data(); + } + + const T* minPtr() const + { + return values.data(); + } + + T* maxPtr() + { + return values.data() + CountV; + } + + const T* maxPtr() const + { + return values.data() + CountV; + } + + }; + public: + + template + requires (std::same_as, hlsl::float32_t3>) + static hlsl::shapes::OBB<> compute(size_t vertexCount, FetchVertexFn&& fetchFn, float epsilon) + { + // Algorithm from Game Engine Gems 2, Fast Computation of Tight-Fitting Oriented Bounding Box + // Credit to Thomas Larsson and Linus Källberg + + constexpr size_t SAMPLE_DIR_COUNT = 7; // Number of sample directions + constexpr size_t SAMPLE_COUNT = SAMPLE_DIR_COUNT * 2; + + struct VertexCollection + { + using FetchFn = std::function; + FetchFn fetch; + size_t size; + + static auto fromSpan(std::span vertices) -> VertexCollection + { + return VertexCollection{ + .fetch = [data = vertices.data()](size_t vertexIndex)-> hlsl::float32_t3 + { + return data[vertexIndex]; + }, + .size = vertices.size() + }; + } + + hlsl::float32_t3 operator[](size_t index) const { return fetch(index); } + }; + + VertexCollection vertices = { + .fetch = std::forward(fetchFn), + .size = vertexCount, + }; + + if (vertices.size <= 0) + { + return hlsl::shapes::OBB<>::createAxisAligned({}, {}); + } + + static auto getQualityValue = [](hlsl::float32_t3 len) -> hlsl::float32_t + { + return len.x * len.y + len.x * len.z + len.y * len.z; //half box area + }; + + using ExtremalVertices = Extremals; + using ExtremalProjections = Extremals; + using Axes = std::array; + using Edges = std::array; + + struct ExtremalSamples + { + ExtremalVertices vertices; + ExtremalProjections projections; + }; + + struct LargeBaseTriangle + { + hlsl::float32_t3 normal = {}; + Axes vertices = {}; + Edges edges = {}; + enum Flag + { + NORMAL, + SECOND_POINT_CLOSE, + THIRD_POINT_CLOSE + } flag; + }; + + static auto findExtremals_7FixedDirs = [](const VertexCollection& vertices)-> ExtremalSamples + { + ExtremalSamples result; + hlsl::float32_t proj; + + const auto firstVertex = vertices.fetch(0); + + auto* minProjections = result.projections.minPtr(); + auto* maxProjections = result.projections.maxPtr(); + + auto* minVertices = result.vertices.minPtr(); + auto* maxVertices = result.vertices.maxPtr(); + + // Slab 0: dir {1, 0, 0} + proj = firstVertex.x; + minProjections[0] = maxProjections[0] = proj; + minVertices[0] = firstVertex; maxVertices[0] = firstVertex; + // Slab 1: dir {0, 1, 0} + proj = firstVertex.y; + minProjections[1] = maxProjections[1] = proj; + minVertices[1] = firstVertex; maxVertices[1] = firstVertex; + // Slab 2: dir {0, 0, 1} + proj = firstVertex.z; + minProjections[2] = maxProjections[2] = proj; + minVertices[2] = firstVertex; maxVertices[2] = firstVertex; + // Slab 3: dir {1, 1, 1} + proj = firstVertex.x + firstVertex.y + firstVertex.z; + minProjections[3] = maxProjections[3] = proj; + minVertices[3] = firstVertex; maxVertices[3] = firstVertex; + // Slab 4: dir {1, 1, -1} + proj = firstVertex.x + firstVertex.y - firstVertex.z; + minProjections[4] = maxProjections[4] = proj; + minVertices[4] = firstVertex; maxVertices[4] = firstVertex; + // Slab 5: dir {1, -1, 1} + proj = firstVertex.x - firstVertex.y + firstVertex.z; + minProjections[5] = maxProjections[5] = proj; + minVertices[5] = firstVertex; maxVertices[5] = firstVertex; + // Slab 6: dir {1, -1, -1} + proj = firstVertex.x - firstVertex.y - firstVertex.z; + minProjections[6] = maxProjections[6] = proj; + minVertices[6] = firstVertex; maxVertices[6] = firstVertex; + + for (size_t vertex_i = 1; vertex_i < vertices.size; vertex_i++) + { + const auto vertex = vertices.fetch(vertex_i); + // Slab 0: dir {1, 0, 0} + proj = vertices.fetch(vertex_i).x; + if (proj < minProjections[0]) { minProjections[0] = proj; minVertices[0] = vertices.fetch(vertex_i); } + if (proj > maxProjections[0]) { maxProjections[0] = proj; maxVertices[0] = vertices.fetch(vertex_i); } + // Slab 1: dir {0, 1, 0} + proj = vertices.fetch(vertex_i).y; + if (proj < minProjections[1]) { minProjections[1] = proj; minVertices[1] = vertices.fetch(vertex_i); } + if (proj > maxProjections[1]) { maxProjections[1] = proj; maxVertices[1] = vertices.fetch(vertex_i); } + // Slab 2: dir {0, 0, 1} + proj = vertices.fetch(vertex_i).z; + if (proj < minProjections[2]) { minProjections[2] = proj; minVertices[2] = vertices.fetch(vertex_i); } + if (proj > maxProjections[2]) { maxProjections[2] = proj; maxVertices[2] = vertices.fetch(vertex_i); } + // Slab 3: dir {1, 1, 1} + proj = vertices.fetch(vertex_i).x + vertices.fetch(vertex_i).y + vertices.fetch(vertex_i).z; + if (proj < minProjections[3]) { minProjections[3] = proj; minVertices[3] = vertices.fetch(vertex_i); } + if (proj > maxProjections[3]) { maxProjections[3] = proj; maxVertices[3] = vertices.fetch(vertex_i); } + // Slab 4: dir {1, 1, -1} + proj = vertices.fetch(vertex_i).x + vertices.fetch(vertex_i).y - vertices.fetch(vertex_i).z; + if (proj < minProjections[4]) { minProjections[4] = proj; minVertices[4] = vertices.fetch(vertex_i); } + if (proj > maxProjections[4]) { maxProjections[4] = proj; maxVertices[4] = vertices.fetch(vertex_i); } + // Slab 5: dir {1, -1, 1} + proj = vertices.fetch(vertex_i).x - vertices.fetch(vertex_i).y + vertices.fetch(vertex_i).z; + if (proj < minProjections[5]) { minProjections[5] = proj; minVertices[5] = vertices.fetch(vertex_i); } + if (proj > maxProjections[5]) { maxProjections[5] = proj; maxVertices[5] = vertices.fetch(vertex_i); } + // Slab 6: dir {1, -1, -1} + proj = vertices.fetch(vertex_i).x - vertices.fetch(vertex_i).y - vertices.fetch(vertex_i).z; + if (proj < minProjections[6]) { minProjections[6] = proj; minVertices[6] = vertices.fetch(vertex_i); } + if (proj > maxProjections[6]) { maxProjections[6] = proj; maxVertices[6] = vertices.fetch(vertex_i); } + } + + return result; + }; + + static auto getSqDist = [](hlsl::float32_t3 a, hlsl::float32_t3 b) -> hlsl::float32_t + { + return hlsl::dot(a - b, a - b); + }; + + static auto findFurthestPointPair = [](const ExtremalVertices& extremalVertices) -> std::pair + { + int indexFurthestPair = 0; + auto maxSqDist = getSqDist(extremalVertices.maxPtr()[0], extremalVertices.minPtr()[0]); + for (int k = 1; k < SAMPLE_DIR_COUNT; k++) + { + const auto sqDist = getSqDist(extremalVertices.maxPtr()[k], extremalVertices.minPtr()[k]); + if (sqDist > maxSqDist) { maxSqDist = sqDist; indexFurthestPair = k; } + } + return { + extremalVertices.minPtr()[indexFurthestPair], + extremalVertices.maxPtr()[indexFurthestPair] + }; + }; + + static auto getSqDistPointInfiniteEdge = [](const hlsl::float32_t3& q, const hlsl::float32_t3& p0, const hlsl::float32_t3& v) -> hlsl::float32_t + { + const auto u0 = q - p0; + const auto t = dot(v, u0); + const auto sqLen_v = hlsl::dot(v, v); + return hlsl::dot(u0, u0) - (t * t) / sqLen_v; + }; + + static auto findFurthestPointFromInfiniteEdge = [](const hlsl::float32_t3& p0, const hlsl::float32_t3& e0, const VertexCollection& vertices) + { + auto maxSqDist = getSqDistPointInfiniteEdge(vertices[0], p0, e0); + int maxIndex = 0; + for (size_t i = 1; i < vertices.size; i++) + { + const auto sqDist = getSqDistPointInfiniteEdge(vertices[i], p0, e0); + if (sqDist > maxSqDist) + { + maxSqDist = sqDist; + maxIndex = i; + } + } + + struct Result + { + hlsl::float32_t3 point; + hlsl::float32_t sqDist; + }; + return Result{ + vertices[maxIndex], + maxSqDist + }; + }; + + static auto findExtremalProjs_OneDir = [](const hlsl::float32_t3& normal, const VertexCollection& vertices) + { + const auto firstProj = hlsl::dot(vertices[0], normal); + auto tMinProj = firstProj, tMaxProj = firstProj; + + for (int i = 1; i < vertices.size; i++) + { + const auto proj = hlsl::dot(vertices[i], normal); + if (proj < tMinProj) { tMinProj = proj; } + if (proj > tMaxProj) { tMaxProj = proj; } + } + + struct Result + { + hlsl::float32_t minProj; + hlsl::float32_t maxProj; + }; + return Result{ tMinProj, tMaxProj }; + }; + + static auto findExtremalPoints_OneDir = [](const hlsl::float32_t3& normal, const VertexCollection& vertices) + { + const auto firstProj = dot(vertices[0], normal); + + auto tMinProj = firstProj, tMaxProj = firstProj; + auto tMinVert = vertices[0], tMaxVert = vertices[0]; + + for (int i = 1; i < vertices.size; i++) + { + const auto proj = hlsl::dot(vertices[i], normal); + if (proj < tMinProj) { tMinProj = proj; tMinVert = vertices[i]; } + if (proj > tMaxProj) { tMaxProj = proj; tMaxVert = vertices[i]; } + } + + struct Result + { + hlsl::float32_t minProj; + hlsl::float32_t maxProj; + hlsl::float32_t3 minVert; + hlsl::float32_t3 maxVert; + }; + return Result{ tMinProj, tMaxProj, tMinVert, tMaxVert }; + }; + + static auto findUpperLowerTetraPoints = [epsilon]( + const hlsl::float32_t3& n, + const VertexCollection& vertices, + const hlsl::float32_t3& p0) + { + const auto extremalPoints = findExtremalPoints_OneDir(n, vertices); + const auto triProj = hlsl::dot(p0, n); + + const auto maxVert = extremalPoints.maxProj - epsilon > triProj ? std::optional(extremalPoints.maxVert) : std::nullopt; + const auto minVert = extremalPoints.minProj + epsilon < triProj ? std::optional(extremalPoints.minVert) : std::nullopt; + + struct Result + { + std::optional minVert; + std::optional maxVert; + }; + return Result{ + minVert, + maxVert + }; + }; + + static auto findBestObbAxesFromTriangleNormalAndEdgeVectors = []( + const VertexCollection& vertices, + const hlsl::float32_t3 normal, + const std::array& edges, + Axes& bestAxes, + hlsl::float32_t& bestVal) + { + // The operands are assumed to be orthogonal and unit normals + const auto yExtremeProjs = findExtremalProjs_OneDir(normal, vertices); + const auto yLen = yExtremeProjs.maxProj - yExtremeProjs.minProj; + + for (const auto& edge : edges) + { + const auto binormal = hlsl::cross(edge, normal); + + const auto xExtremeProjs = findExtremalProjs_OneDir(edge, vertices); + const auto xLen = xExtremeProjs.maxProj - xExtremeProjs.minProj; + + const auto zExtremeProjs = findExtremalProjs_OneDir(binormal, vertices); + const auto zLen = zExtremeProjs.maxProj - zExtremeProjs.minProj; + + const auto quality = getQualityValue({xLen, yLen, zLen}); + if (quality < bestVal) + { + bestVal = quality; + bestAxes = { + edge, + normal, + binormal + }; + } + } + + }; + + + static auto findBaseTriangle = [epsilon](const ExtremalVertices& extremalVertices, const VertexCollection& vertices)-> LargeBaseTriangle + { + std::array baseTriangleVertices = {}; // p0, p1, p2 + Edges edges; + + // Find the furthest point pair among the selected min and max point pairs + std::tie(baseTriangleVertices[0], baseTriangleVertices[1]) = findFurthestPointPair(extremalVertices); + + // Degenerate case 1: + // no need to compute third vertices, since base triangle is invalid + if (getSqDist(baseTriangleVertices[0], baseTriangleVertices[1]) < epsilon) + { + return { + .vertices = baseTriangleVertices, + .flag = LargeBaseTriangle::SECOND_POINT_CLOSE + }; + } + + // Compute edge vector of the line segment p0, p1 + edges[0] = hlsl::normalize(baseTriangleVertices[0] - baseTriangleVertices[1]); + + // Find a third point furthest away from line given by p0, e0 to define the large base triangle + const auto furthestPointRes = findFurthestPointFromInfiniteEdge(baseTriangleVertices[0], edges[0], vertices); + baseTriangleVertices[2] = furthestPointRes.point; + + // Degenerate case 2: + if (furthestPointRes.sqDist < epsilon) + { + return { + .vertices = baseTriangleVertices, + .edges = edges, + .flag = LargeBaseTriangle::THIRD_POINT_CLOSE + }; + } + + // Compute the two remaining edge vectors and the normal vector of the base triangle + edges[1] = hlsl::normalize(baseTriangleVertices[1] - baseTriangleVertices[2]); + edges[2] = hlsl::normalize(baseTriangleVertices[2] - baseTriangleVertices[0]); + const auto normal = hlsl::normalize(hlsl::cross(edges[1], edges[0])); + + return { + .normal = normal, + .vertices = baseTriangleVertices, + .edges = edges, + .flag = LargeBaseTriangle::NORMAL + }; + }; + + auto findImprovedObbAxesFromUpperAndLowerTetrasOfBaseTriangle = [](const VertexCollection& vertices, + const LargeBaseTriangle& baseTriangle, + Axes& bestAxes, hlsl::float32_t& bestVal) + { + + // Find furthest points above and below the plane of the base triangle for tetra constructions + // For each found valid point, search for the best OBB axes based on the 3 arising triangles + const auto upperLowerTetraVertices = findUpperLowerTetraPoints(baseTriangle.normal, vertices, baseTriangle.vertices[0]); + if (upperLowerTetraVertices.minVert) + { + const auto minVert = *upperLowerTetraVertices.minVert; + const auto f0 = normalize(minVert - baseTriangle.vertices[0]); + const auto f1 = normalize(minVert - baseTriangle.vertices[1]); + const auto f2 = normalize(minVert - baseTriangle.vertices[2]); + const auto n0 = normalize(cross(f1, baseTriangle.edges[0])); + const auto n1 = normalize(cross(f2, baseTriangle.edges[1])); + const auto n2 = normalize(cross(f0, baseTriangle.edges[2])); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n0, { baseTriangle.edges[0], f1, f0 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n1, { baseTriangle.edges[1], f2, f1 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n2, { baseTriangle.edges[2], f0, f2 }, bestAxes, bestVal); + } + if (upperLowerTetraVertices.maxVert) + { + const auto maxVert = *upperLowerTetraVertices.maxVert; + const auto f0 = normalize(maxVert - baseTriangle.vertices[0]); + const auto f1 = normalize(maxVert - baseTriangle.vertices[1]); + const auto f2 = normalize(maxVert - baseTriangle.vertices[2]); + const auto n0 = normalize(cross(f1, baseTriangle.edges[0])); + const auto n1 = normalize(cross(f2, baseTriangle.edges[1])); + const auto n2 = normalize(cross(f0, baseTriangle.edges[2])); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n0, { baseTriangle.edges[0], f1, f0 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n1, { baseTriangle.edges[1], f2, f1 }, bestAxes, bestVal); + findBestObbAxesFromTriangleNormalAndEdgeVectors(vertices, n2, { baseTriangle.edges[2], f0, f2 }, bestAxes, bestVal); + } + }; + + static auto buildObbFromAxesAndLocalMinMax = []( + const Axes& axes, + const hlsl::float32_t3& localMin, + const hlsl::float32_t3& localMax) -> hlsl::shapes::OBB<3, hlsl::float32_t> + { + const auto localMid = 0.5f * (localMin + localMax); + const hlsl::float32_t3 axesArray[3] = {axes[0], axes[1], axes[2]}; + return hlsl::shapes::OBB<3, hlsl::float32_t>::create( + axes[0] * localMid.x + axes[1] * localMid.y + axes[2] * localMid.z, + 0.5f * (localMax - localMin), + axesArray + ); + }; + + static auto computeObb = [](const Axes& axes, const VertexCollection& vertices, hlsl::float32_t& quality) + { + const auto extremalX = findExtremalProjs_OneDir(axes[0], vertices); + const auto extremalY = findExtremalProjs_OneDir(axes[1], vertices); + const auto extremalZ = findExtremalProjs_OneDir(axes[2], vertices); + const auto localMin = hlsl::float32_t3{ extremalX.minProj, extremalY.minProj, extremalZ.minProj }; + const auto localMax = hlsl::float32_t3{ extremalX.maxProj, extremalY.maxProj, extremalZ.maxProj }; + quality = getQualityValue(localMax - localMin); + return buildObbFromAxesAndLocalMinMax(axes, localMin, localMax); + }; + + static auto computeLineAlignedObb = [epsilon](const hlsl::float32_t3& u, const VertexCollection& vertices) + { + // Given u, build any orthonormal base u, v, w + + // Make sure r is not equal to u + auto r = u; + if (fabs(u.x) > fabs(u.y) && fabs(u.x) > fabs(u.z)) { r.x = 0; } + else if (fabs(u.y) > fabs(u.z)) { r.y = 0; } + else { r.z = 0; } + + const auto sqLen = hlsl::dot(r, r); + if (sqLen < epsilon) { r.x = r.y = r.z = 1; } + + const auto v = normalize(cross(u, r)); + const auto w = normalize(cross(u, v)); + hlsl::float32_t quality; + return computeObb({ u, v, w }, vertices, quality); + }; + + const auto extremals = findExtremals_7FixedDirs(vertices); + + const auto* minProj = extremals.projections.minPtr(); + const auto* maxProj = extremals.projections.maxPtr(); + + // Determine which points to use in the iterations below + const auto selectedVertices = [&] + { + if (vertices.size < SAMPLE_COUNT) { return vertices; } + return VertexCollection::fromSpan(extremals.vertices.values); + }(); + + // Compute size of AABB (max and min projections of vertices are already computed as slabs 0-2) + auto alMid = hlsl::float32_t3((minProj[0] + maxProj[0]) * 0.5f, (minProj[1] + maxProj[1]) * 0.5f, (minProj[2] + maxProj[2]) * 0.5f); + auto alLen = hlsl::float32_t3(maxProj[0] - minProj[0], maxProj[1] - minProj[1], maxProj[2] - minProj[2]); + auto alVal = getQualityValue(alLen); + + + const auto baseTriangle = findBaseTriangle(extremals.vertices, vertices); + + // Degenerate case 1: + // If the found furthest points are located very close, return OBB aligned with the initial AABB + if (baseTriangle.flag == LargeBaseTriangle::SECOND_POINT_CLOSE) + return hlsl::shapes::OBB<>::createAxisAligned(alMid, alLen); + + // Degenerate case 2: + // If the third point is located very close to the line, return an OBB aligned with the line + if (baseTriangle.flag == LargeBaseTriangle::THIRD_POINT_CLOSE) + return computeLineAlignedObb(baseTriangle.edges[0], vertices); + + + Axes bestAxes = { + hlsl::float32_t3{1.f, 0.f, 0.f}, + {0.f, 1.f, 0.f}, + {0.f, 0.f, 1.f}, + }; + auto bestVal = alVal; + // Find best OBB axes based on the base triangle + findBestObbAxesFromTriangleNormalAndEdgeVectors(selectedVertices, baseTriangle.normal, baseTriangle.edges, bestAxes, bestVal); + + // Find improved OBB axes based on constructed di-tetrahedral shape raised from base triangle + findImprovedObbAxesFromUpperAndLowerTetrasOfBaseTriangle(selectedVertices, baseTriangle, bestAxes, bestVal); + + hlsl::float32_t improvedObbQuality; + const auto obb = computeObb(bestAxes, vertices, improvedObbQuality); + + // Check if the OBB extent is still smaller than the intial AABB + if (improvedObbQuality < alVal) + return obb; + return hlsl::shapes::OBB<>::createAxisAligned(alMid, alLen); + + } + +}; + +} + +#endif diff --git a/include/nbl/asset/utils/CPolygonGeometryManipulator.h b/include/nbl/asset/utils/CPolygonGeometryManipulator.h index cc41bba7e9..4a31bd6a95 100644 --- a/include/nbl/asset/utils/CPolygonGeometryManipulator.h +++ b/include/nbl/asset/utils/CPolygonGeometryManipulator.h @@ -10,6 +10,8 @@ #include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/utils/CGeometryManipulator.h" #include "nbl/asset/utils/CSmoothNormalGenerator.h" +#include "nbl/asset/utils/COBBGenerator.h" +#include "nbl/builtin/hlsl/shapes/obb.hlsl" namespace nbl::asset { @@ -231,6 +233,13 @@ class NBL_API2 CPolygonGeometryManipulator EEM_COUNT }; + template + requires (std::same_as, hlsl::float32_t3>) + static inline hlsl::shapes::OBB<3, hlsl::float32_t> calculateOBB(size_t vertexCount, FetchVertexFn&& fetchFn, float epsilon = 1.525e-5f) + { + return COBBGenerator::compute(vertexCount, std::forward(fetchFn), epsilon); + } + static core::smart_refctd_ptr createUnweldedList(const ICPUPolygonGeometry* inGeo); using SSNGVertexData = CSmoothNormalGenerator::VertexData; @@ -498,7 +507,6 @@ class NBL_API2 CPolygonGeometryManipulator static float DistanceToLine(core::vectorSIMDf P0, core::vectorSIMDf P1, core::vectorSIMDf InPoint); static float DistanceToPlane(core::vectorSIMDf InPoint, core::vectorSIMDf PlanePoint, core::vectorSIMDf PlaneNormal); - static core::matrix3x4SIMD calculateOBB(const nbl::asset::ICPUMeshBuffer* meshbuffer); //! Calculates bounding box of the meshbuffer static inline core::aabbox3df calculateBoundingBox( diff --git a/include/nbl/asset/utils/CQuantQuaternionCache.h b/include/nbl/asset/utils/CQuantQuaternionCache.h index 8e46dffb0a..c4b13e9c38 100644 --- a/include/nbl/asset/utils/CQuantQuaternionCache.h +++ b/include/nbl/asset/utils/CQuantQuaternionCache.h @@ -7,6 +7,7 @@ #include "nbl/asset/utils/CDirQuantCacheBase.h" +#include "nbl/builtin/hlsl/math/quaternions.hlsl" namespace nbl @@ -60,7 +61,7 @@ class CQuantQuaternionCache : public CDirQuantCacheBase - value_type_t quantize(const core::quaternion& quat) + value_type_t quantize(const hlsl::math::quaternion& quat) { return Base::quantize<4u,CacheFormat>(reinterpret_cast(quat)); } @@ -68,4 +69,4 @@ class CQuantQuaternionCache : public CDirQuantCacheBase extraDefines = {}; E_SPIRV_VERSION targetSpirvVersion = E_SPIRV_VERSION::ESV_1_6; + bool depfile = false; + system::path depfilePath = {}; }; // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#debugging @@ -215,6 +217,10 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted // Needed for json vector serialization. Making it private and declaring from_json(_, SEntry&) as friend didn't work inline SPreprocessingDependency() {} + inline const system::path& getRequestingSourceDir() const { return requestingSourceDir; } + inline std::string_view getIdentifier() const { return identifier; } + inline bool isStandardInclude() const { return standardInclude; } + private: friend void to_json(nlohmann::json& j, const SEntry::SPreprocessingDependency& dependency); friend void from_json(const nlohmann::json& j, SEntry::SPreprocessingDependency& dependency); @@ -447,6 +453,17 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted NBL_API2 EntrySet::const_iterator find_impl(const SEntry& mainFile, const CIncludeFinder* finder) const; }; + struct DepfileWriteParams + { + system::ISystem* system = nullptr; + std::string_view depfilePath = {}; + std::string_view outputPath = {}; + std::string_view sourceIdentifier = {}; + system::path workingDirectory = {}; + }; + + static bool writeDepfile(const DepfileWriteParams& params, const CCache::SEntry::dependency_container_t& dependencies, const CIncludeFinder* includeFinder = nullptr, system::logger_opt_ptr logger = nullptr); + core::smart_refctd_ptr compileToSPIRV(const std::string_view code, const SCompilerOptions& options) const; inline core::smart_refctd_ptr compileToSPIRV(const char* code, const SCompilerOptions& options) const diff --git a/include/nbl/builtin/glsl/ies/functions.glsl b/include/nbl/builtin/glsl/ies/functions.glsl index 70f5165f98..54c266d114 100644 --- a/include/nbl/builtin/glsl/ies/functions.glsl +++ b/include/nbl/builtin/glsl/ies/functions.glsl @@ -6,25 +6,33 @@ #include -// TODO: implement proper mirroing -// MIRROR_180_BITS = 0b001, Last Angle is 180, so map V slightly differently -// MIRROR_90_BITS = 0b010, Last Angle is 90, so map both U and V slightly differently -// ISOTROPIC_BITS = 0b011, texture to sample is Nx1, pretend v=middle always -// FULL_THETA_BIT = 0b100, handle extended domain and rotate by 45 degrees for anisotropic - -vec2 nbl_glsl_IES_convert_dir_to_uv(vec3 dir) { - float sum = dot(vec3(1.0f), abs(dir)); +// TODO: when rewriting to HLSL this is not IES namespace or folder, this should be octahedral mapping sitting somewhere where the spherical/polar sits +// NOTE: I changed it to return NDC [-1,1]^2 instead of UV coords [0,1]^2 +vec2 nbl_glsl_TODOnamespace_octahedral_mapping(vec3 dir) +{ + float sum = dot(vec3(1.0f), abs(dir)); vec3 s = dir / sum; - if(s.z < 0.0f) { - s.xy = sign(s.xy) * (1.0f - abs(s.yx)); + if(s.z < 0.0f) + { + const uvec2 flipSignMask = floatBitsToUint(s.xy)&0x80000000u; + s.xy = uintBitsToFloat(floatBitsToUint(1.0f - abs(s.yx))^flipSignMask); } - return s.xy * 0.5f + 0.5f; + return s.xy; } -// vec2 nbl_glsl_IES_convert_dir_to_uv(vec3 dir) { -// return vec2((atan(dir.x, dir.y) + nbl_glsl_PI) / (2.0*nbl_glsl_PI), acos(dir.z) / nbl_glsl_PI); -// } +// TODO: implement proper mirroing +// MIRROR_180_BITS = 0b001, Last Angle is 180, so map V with MIRROR and corner sampling off +// MIRROR_90_BITS = 0b010, Last Angle is 90, so map both U and V with MIRROR and corner sampling off +// ISOTROPIC_BITS = 0b011, texture to sample is Nx1, pretend v=middle always , and make u REPEAT or CLAMP_TO_BORDER +// FULL_THETA_BIT = 0b100, handle truncated domain and rotate by 45 degrees for anisotropic +// (certain combos wont work like 90 degree 2 symmetry domain & half theta), it really needs to be an 8 case label thing explicitly enumerated +vec2 nbl_glsl_IES_convert_dir_to_uv(vec3 dir, vec2 halfMinusHalfPixel) +{ + // halfMinusHalfPixel = 0.5-0.5/texSize + // believe it or not, cornerSampled(NDC*0.5+0.5) = NDC*0.5*(1-1/texSize)+0.5 + return nbl_glsl_TODOnamespace_octahedral_mapping(dir)*halfMinusHalfPixel+0.5; +} #endif \ No newline at end of file diff --git a/include/nbl/builtin/glsl/material_compiler/common.glsl b/include/nbl/builtin/glsl/material_compiler/common.glsl index da67b12cbf..c0a52a4f0d 100644 --- a/include/nbl/builtin/glsl/material_compiler/common.glsl +++ b/include/nbl/builtin/glsl/material_compiler/common.glsl @@ -215,6 +215,7 @@ bool nbl_glsl_MC_op_isDelta(in uint op) #ifdef TEX_PREFETCH_STREAM #include #endif +// TODO: once rewritten to HLSL, shall use new API #include // OptiX likes this one better @@ -601,7 +602,8 @@ vec3 nbl_glsl_MC_oriented_material_t_getEmissive(in nbl_glsl_MC_oriented_materia if ((floatBitsToInt(emitter.orientation[0])&1u) != 1u) { right *= -1; } - return emissive * nbl_glsl_vTextureGrad(emitter.emissionProfile, nbl_glsl_IES_convert_dir_to_uv(mat3(right, up, view)*dir), mat2(0.0)).r; + vec2 halfMinusHalfPixel = vec2(0.5)-vec2(0.5)/vec2(nbl_glsl_unpackSize(emitter.emissionProfile)); + return emissive * nbl_glsl_vTextureGrad(emitter.emissionProfile, nbl_glsl_IES_convert_dir_to_uv(mat3(right, up, view)*dir,halfMinusHalfPixel), mat2(0.0)).r; } #endif return emissive; @@ -1498,4 +1500,4 @@ nbl_glsl_MC_quot_pdf_aov_t nbl_glsl_MC_runGenerateAndRemainderStream( } #endif //GEN_CHOICE_STREAM -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/algorithm.hlsl b/include/nbl/builtin/hlsl/algorithm.hlsl index 7eca7d51df..66442a11a1 100644 --- a/include/nbl/builtin/hlsl/algorithm.hlsl +++ b/include/nbl/builtin/hlsl/algorithm.hlsl @@ -19,7 +19,7 @@ namespace impl // TODO: use structs template - NBL_CONSTEXPR_INLINE_FUNC void swap(NBL_REF_ARG(T) lhs, NBL_REF_ARG(T) rhs) + NBL_CONSTEXPR_FUNC void swap(NBL_REF_ARG(T) lhs, NBL_REF_ARG(T) rhs) { T tmp = lhs; lhs = rhs; @@ -27,7 +27,7 @@ namespace impl } template<> - NBL_CONSTEXPR_INLINE_FUNC void swap(NBL_REF_ARG(uint16_t) lhs, NBL_REF_ARG(uint16_t) rhs) + NBL_CONSTEXPR_FUNC void swap(NBL_REF_ARG(uint16_t) lhs, NBL_REF_ARG(uint16_t) rhs) { lhs ^= rhs; rhs ^= lhs; @@ -35,7 +35,7 @@ namespace impl } template<> - NBL_CONSTEXPR_INLINE_FUNC void swap(NBL_REF_ARG(uint32_t) lhs, NBL_REF_ARG(uint32_t) rhs) + NBL_CONSTEXPR_FUNC void swap(NBL_REF_ARG(uint32_t) lhs, NBL_REF_ARG(uint32_t) rhs) { lhs ^= rhs; rhs ^= lhs; @@ -43,7 +43,7 @@ namespace impl } template<> - NBL_CONSTEXPR_INLINE_FUNC void swap(NBL_REF_ARG(uint64_t) lhs, NBL_REF_ARG(uint64_t) rhs) + NBL_CONSTEXPR_FUNC void swap(NBL_REF_ARG(uint64_t) lhs, NBL_REF_ARG(uint64_t) rhs) { lhs ^= rhs; rhs ^= lhs; @@ -51,7 +51,7 @@ namespace impl } template<> - NBL_CONSTEXPR_INLINE_FUNC void swap(NBL_REF_ARG(int16_t) lhs, NBL_REF_ARG(int16_t) rhs) + NBL_CONSTEXPR_FUNC void swap(NBL_REF_ARG(int16_t) lhs, NBL_REF_ARG(int16_t) rhs) { lhs ^= rhs; rhs ^= lhs; @@ -59,7 +59,7 @@ namespace impl } template<> - NBL_CONSTEXPR_INLINE_FUNC void swap(NBL_REF_ARG(int32_t) lhs, NBL_REF_ARG(int32_t) rhs) + NBL_CONSTEXPR_FUNC void swap(NBL_REF_ARG(int32_t) lhs, NBL_REF_ARG(int32_t) rhs) { lhs ^= rhs; rhs ^= lhs; @@ -67,7 +67,7 @@ namespace impl } template<> - NBL_CONSTEXPR_INLINE_FUNC void swap(NBL_REF_ARG(int64_t) lhs, NBL_REF_ARG(int64_t) rhs) + NBL_CONSTEXPR_FUNC void swap(NBL_REF_ARG(int64_t) lhs, NBL_REF_ARG(int64_t) rhs) { lhs ^= rhs; rhs ^= lhs; @@ -75,7 +75,7 @@ namespace impl } #else template - NBL_CONSTEXPR_INLINE_FUNC void swap(NBL_REF_ARG(T) lhs, NBL_REF_ARG(T) rhs) + NBL_CONSTEXPR_FUNC void swap(NBL_REF_ARG(T) lhs, NBL_REF_ARG(T) rhs) { std::swap(lhs, rhs); } @@ -83,7 +83,7 @@ namespace impl } template -NBL_CONSTEXPR_INLINE_FUNC void swap(NBL_REF_ARG(T) lhs, NBL_REF_ARG(T) rhs) +NBL_CONSTEXPR_FUNC void swap(NBL_REF_ARG(T) lhs, NBL_REF_ARG(T) rhs) { impl::swap(lhs, rhs); } diff --git a/include/nbl/builtin/hlsl/array_accessors.hlsl b/include/nbl/builtin/hlsl/array_accessors.hlsl index b025f9d677..73a4b83102 100644 --- a/include/nbl/builtin/hlsl/array_accessors.hlsl +++ b/include/nbl/builtin/hlsl/array_accessors.hlsl @@ -24,7 +24,8 @@ struct array_set arr[index] = val; } }; + } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/blit/default_blit.comp.hlsl b/include/nbl/builtin/hlsl/blit/default_blit.comp.hlsl index 1407d7fc77..4b97bbc08f 100644 --- a/include/nbl/builtin/hlsl/blit/default_blit.comp.hlsl +++ b/include/nbl/builtin/hlsl/blit/default_blit.comp.hlsl @@ -59,6 +59,7 @@ using namespace nbl::hlsl::blit; // TODO: push constants [numthreads(ConstevalParameters::WorkGroupSize,1,1)] +[shader("compute")] void main() { InImgAccessor inImgA; diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 5e5e543791..d70e8823da 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -280,7 +280,7 @@ struct SCookTorrance const scalar_type NdotV = localV.z; fresnel_type _f = __getOrientedFresnel(fresnel, NdotV); - fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); + fresnel::OrientedEtaRcps rcpEta = _f.getRefractionOrientedEtaRcps(); const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(NdotV)); const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy); @@ -304,7 +304,8 @@ struct SCookTorrance scalar_type rcpChoiceProb; scalar_type z = u.z; sampling::PartitionRandVariable partitionRandVariable; - bool transmitted = partitionRandVariable(reflectance, z, rcpChoiceProb); + partitionRandVariable.leftProb = reflectance; + bool transmitted = partitionRandVariable(z, rcpChoiceProb); const scalar_type LdotH = hlsl::mix(VdotH, ieee754::copySign(hlsl::sqrt(rcpEta.value2[0]*VdotH*VdotH + scalar_type(1.0) - rcpEta.value2[0]), -VdotH), transmitted); bool valid; diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index f7655e9978..33faa79efc 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -313,14 +313,18 @@ NBL_CONCEPT_BEGIN(2) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::eta_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel(cosTheta)), ::nbl::hlsl::is_same_v, typename T::vector_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getOrientedEtaRcps()), ::nbl::hlsl::is_same_v, OrientedEtaRcps)) ); #undef cosTheta #undef fresnel #include +namespace impl +{ +template +NBL_BOOL_CONCEPT VectorIsMonochrome = vector_traits::Dimension == 1; +} + #define NBL_CONCEPT_NAME TwoSidedFresnel #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -331,8 +335,11 @@ NBL_CONCEPT_BEGIN(2) #define cosTheta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Fresnel, T)) + ((NBL_CONCEPT_REQ_TYPE)(T::eta_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getRefractionOrientedEta()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getRefractionOrientedEtaRcps()), ::nbl::hlsl::is_same_v, OrientedEtaRcps)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getReorientedFresnel(cosTheta)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VectorIsMonochrome, typename T::eta_type)) ); #undef cosTheta #undef fresnel @@ -362,7 +369,7 @@ struct Schlick return F0 + (1.0 - F0) * x*x*x*x*x; } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + OrientedEtaRcps getRefractionOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { const eta_type sqrtF0 = hlsl::sqrt(F0); OrientedEtaRcps rcpEta; @@ -424,13 +431,13 @@ struct Conductor return (rs2 + rp2) * hlsl::promote(0.5); } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC - { - OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / eta; - rcpEta.value2 = rcpEta.value * rcpEta.value; - return rcpEta; - } + // OrientedEtaRcps getRefractionOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + // { + // OrientedEtaRcps rcpEta; + // rcpEta.value = hlsl::promote(1.0) / eta; + // rcpEta.value2 = rcpEta.value * rcpEta.value; + // return rcpEta; + // } T eta; T etak2; @@ -484,7 +491,7 @@ struct Dielectric // default to monochrome, but it is possible to have RGB fresnel without dispersion fixing the refraction Eta // to be something else than the etas used to compute RGB reflectance or some sort of interpolation of them scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return orientedEta.value[0]; } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { return orientedEta.getReciprocals(); } + OrientedEtaRcps getRefractionOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { return orientedEta.getReciprocals(); } Dielectric getReorientedFresnel(const scalar_type NdotI) NBL_CONST_MEMBER_FUNC { @@ -508,25 +515,26 @@ struct iridescent_helper using scalar_type = typename vector_traits::scalar_type; using vector_type = T; - // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) - static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) + // returns phi, the phase shift for each plane of polarization (p,s) + static void phase_shift(const vector_type ior1, const vector_type ior2, const vector_type iork2, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) { - vector_type cosTheta_2 = cosTheta * cosTheta; - vector_type sinTheta2 = hlsl::promote(1.0) - cosTheta_2; - const vector_type eta2 = orientedEta*orientedEta; - const vector_type etak2 = orientedEtak*orientedEtak; - - vector_type z = eta2 - etak2 - sinTheta2; - vector_type w = hlsl::sqrt(z * z + scalar_type(4.0) * eta2 * eta2 * etak2); - vector_type a2 = (z + w) * hlsl::promote(0.5); - vector_type b2 = (w - z) * hlsl::promote(0.5); - vector_type b = hlsl::sqrt(b2); + const vector_type cosTheta2 = cosTheta * cosTheta; + const vector_type sinTheta2 = hlsl::promote(1.0) - cosTheta2; + const vector_type ior1_2 = ior1*ior1; + const vector_type ior2_2 = ior2*ior2; + const vector_type iork2_2 = iork2*iork2; - const vector_type t0 = eta2 + etak2; - const vector_type t1 = t0 * cosTheta_2; + const vector_type z = ior2_2 * (hlsl::promote(1.0) - iork2_2) - ior1_2 * sinTheta2; + const vector_type w = hlsl::sqrt(z*z + scalar_type(4.0) * ior2_2 * ior2_2 * iork2_2); + const vector_type a2 = hlsl::max(z + w, hlsl::promote(0.0)) * hlsl::promote(0.5); + const vector_type b2 = hlsl::max(w - z, hlsl::promote(0.0)) * hlsl::promote(0.5); + const vector_type a = hlsl::sqrt(a2); + const vector_type b = hlsl::sqrt(b2); - phiS = hlsl::atan2(hlsl::promote(2.0) * b * cosTheta, a2 + b2 - cosTheta_2); - phiP = hlsl::atan2(hlsl::promote(2.0) * eta2 * cosTheta * (hlsl::promote(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); + phiS = hlsl::atan2(scalar_type(2.0) * ior1 * b * cosTheta, a2 + b2 - ior1_2*cosTheta2); + const vector_type k2_plus_one = hlsl::promote(1.0) + iork2_2; + phiP = hlsl::atan2(scalar_type(2.0) * ior1 * ior2_2 * cosTheta * (scalar_type(2.0) * iork2 * a - (hlsl::promote(1.0) - iork2_2) * b), + ior2_2 * cosTheta2 * k2_plus_one * k2_plus_one - ior1_2*(a2+b2)); } // Evaluation XYZ sensitivity curves in Fourier space @@ -544,10 +552,9 @@ struct iridescent_helper } template - static T __call(const vector_type _D, const vector_type eta12, const vector_type eta23, const vector_type etak23, const scalar_type clampedCosTheta) + static T __call(const vector_type _D, const vector_type ior1, const vector_type ior2, const vector_type ior3, const vector_type iork3, + const vector_type eta12, const vector_type eta23, const vector_type etak23, const scalar_type clampedCosTheta) { - const vector_type wavelengths = vector_type(Colorspace::wavelength_R, Colorspace::wavelength_G, Colorspace::wavelength_B); - const scalar_type cosTheta_1 = clampedCosTheta; vector_type R12p, R23p, R12s, R23s; vector_type cosTheta_2; @@ -561,7 +568,7 @@ struct iridescent_helper if (hlsl::any(notTIR)) { - Dielectric::__polarized(eta12, hlsl::promote(cosTheta_1), R12p, R12s); + Dielectric::__polarized(eta12 * eta12, hlsl::promote(cosTheta_1), R12p, R12s); // Reflected part by the base // if kappa==0, base material is dielectric @@ -587,14 +594,13 @@ struct iridescent_helper // Optical Path Difference const vector_type D = _D * cosTheta_2; - const vector_type Dphi = hlsl::promote(2.0 * numbers::pi) * D / wavelengths; vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs; vector_type I = hlsl::promote(0.0); // Evaluate the phase shift - phase_shift(eta12, hlsl::promote(0.0), hlsl::promote(cosTheta_1), phi21p, phi21s); - phase_shift(eta23, etak23, cosTheta_2, phi23p, phi23s); + phase_shift(ior1, ior2, hlsl::promote(0.0), hlsl::promote(cosTheta_1), phi21s, phi21p); + phase_shift(ior2, ior3, iork3, cosTheta_2, phi23s, phi23p); phi21p = hlsl::promote(numbers::pi) - phi21p; phi21s = hlsl::promote(numbers::pi) - phi21s; @@ -615,7 +621,7 @@ struct iridescent_helper NBL_UNROLL for (int m=1; m<=2; ++m) { Cm *= r123p; - Sm = hlsl::promote(2.0) * evalSensitivity(hlsl::promote(m)*D, hlsl::promote(m)*(phi23p+phi21p)); + Sm = hlsl::promote(2.0) * evalSensitivity(hlsl::promote(scalar_type(m))*D, hlsl::promote(scalar_type(m))*(phi23p+phi21p)); I += Cm*Sm; } @@ -629,11 +635,11 @@ struct iridescent_helper NBL_UNROLL for (int m=1; m<=2; ++m) { Cm *= r123s; - Sm = hlsl::promote(2.0) * evalSensitivity(hlsl::promote(m)*D, hlsl::promote(m) *(phi23s+phi21s)); + Sm = hlsl::promote(2.0) * evalSensitivity(hlsl::promote(scalar_type(m))*D, hlsl::promote(scalar_type(m)) *(phi23s+phi21s)); I += Cm*Sm; } - return hlsl::max(colorspace::scRGB::FromXYZ(I), hlsl::promote(0.0)) * hlsl::promote(0.5); + return hlsl::max(Colorspace::FromXYZ(I) * hlsl::promote(0.5), hlsl::promote(0.0)); } }; @@ -643,13 +649,14 @@ struct iridescent_base using scalar_type = typename vector_traits::scalar_type; using vector_type = T; - vector_type getD() NBL_CONST_MEMBER_FUNC { return D; } - vector_type getEta12() NBL_CONST_MEMBER_FUNC { return eta12; } - vector_type getEta23() NBL_CONST_MEMBER_FUNC { return eta23; } - vector_type D; + vector_type ior1; + vector_type ior2; + vector_type ior3; + vector_type iork3; vector_type eta12; // outside (usually air 1.0) -> thin-film IOR vector_type eta23; // thin-film -> base material IOR + vector_type eta13; }; } @@ -679,24 +686,30 @@ struct Iridescent(2.0 * params.Dinc) * params.ior2; + retval.ior1 = params.ior1; + retval.ior2 = params.ior2; + retval.ior3 = params.ior3; + retval.iork3 = params.iork3; retval.eta12 = params.ior2/params.ior1; retval.eta23 = params.ior3/params.ior2; retval.etak23 = params.iork3/params.ior2; + retval.eta13 = params.ior3/params.ior1; return retval; } T operator()(const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC { - return impl::iridescent_helper::template __call(base_type::getD(), base_type::getEta12(), base_type::getEta23(), getEtak23(), clampedCosTheta); + return impl::iridescent_helper::template __call(base_type::D, base_type::ior1, base_type::ior2, base_type::ior3, base_type::iork3, + base_type::eta12, base_type::eta23, getEtak23(), clampedCosTheta); } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC - { - OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / base_type::eta23; - rcpEta.value2 = rcpEta.value * rcpEta.value; - return rcpEta; - } + // OrientedEtaRcps getRefractionOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + // { + // OrientedEtaRcps rcpEta; + // rcpEta.value = hlsl::promote(1.0) / base_type::eta13; + // rcpEta.value2 = rcpEta.value * rcpEta.value; + // return rcpEta; + // } vector_type getEtak23() NBL_CONST_MEMBER_FUNC { @@ -731,21 +744,26 @@ struct Iridescent(2.0 * params.Dinc) * params.ior2; + retval.ior1 = params.ior1; + retval.ior2 = params.ior2; + retval.ior3 = params.ior3; retval.eta12 = params.ior2/params.ior1; retval.eta23 = params.ior3/params.ior2; + retval.eta13 = params.ior3/params.ior1; return retval; } T operator()(const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC { - return impl::iridescent_helper::template __call(base_type::getD(), base_type::getEta12(), base_type::getEta23(), getEtak23(), clampedCosTheta); + return impl::iridescent_helper::template __call(base_type::D, base_type::ior1, base_type::ior2, base_type::ior3, getEtak23(), + base_type::eta12, base_type::eta23, getEtak23(), clampedCosTheta); } - scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return base_type::eta23[0]; } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return base_type::eta13[0]; } + OrientedEtaRcps getRefractionOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / base_type::eta23[0]; + rcpEta.value = hlsl::promote(1.0) / hlsl::promote(base_type::eta13[0]); rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } @@ -755,8 +773,12 @@ struct Iridescent(1.0)/base_type::eta12, flip); - orientedFresnel.eta23 = hlsl::mix(base_type::eta23, hlsl::promote(1.0)/base_type::eta23, flip); + orientedFresnel.ior1 = hlsl::mix(base_type::ior1, base_type::ior3, flip); + orientedFresnel.ior2 = base_type::ior2; + orientedFresnel.ior3 = hlsl::mix(base_type::ior3, base_type::ior1, flip); + orientedFresnel.eta12 = hlsl::mix(base_type::eta12, hlsl::promote(1.0)/base_type::eta23, flip); + orientedFresnel.eta23 = hlsl::mix(base_type::eta23, hlsl::promote(1.0)/base_type::eta12, flip); + orientedFresnel.eta13 = hlsl::mix(base_type::eta13, hlsl::promote(1.0)/base_type::eta13, flip); return orientedFresnel; } diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index 4ad4bb341e..c64f6e3b84 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -406,7 +406,7 @@ template struct is_ggx : impl::is_ggx {}; template -NBL_CONSTEXPR bool is_ggx_v = is_ggx::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_ggx_v = is_ggx::value; } } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index 712b614755..6d5744fb49 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -41,7 +41,8 @@ struct SSmoothDielectric scalar_type rcpChoiceProb; sampling::PartitionRandVariable partitionRandVariable; - bool transmitted = partitionRandVariable(reflectance, u.z, rcpChoiceProb); + partitionRandVariable.leftProb = reflectance; + bool transmitted = partitionRandVariable(u.z, rcpChoiceProb); ray_dir_info_type V = interaction.getV(); Refract r = Refract::create(V.getDirection(), interaction.getN()); @@ -128,7 +129,8 @@ struct SThinSmoothDielectric scalar_type rcpChoiceProb; scalar_type z = u.z; sampling::PartitionRandVariable partitionRandVariable; - const bool transmitted = partitionRandVariable(reflectionProb, z, rcpChoiceProb); + partitionRandVariable.leftProb = reflectionProb; + const bool transmitted = partitionRandVariable(z, rcpChoiceProb); remainderMetadata = hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, transmitted) * rcpChoiceProb; ray_dir_info_type V = interaction.getV(); diff --git a/include/nbl/builtin/hlsl/complex.hlsl b/include/nbl/builtin/hlsl/complex.hlsl index da04c49b51..7e8f6526ec 100644 --- a/include/nbl/builtin/hlsl/complex.hlsl +++ b/include/nbl/builtin/hlsl/complex.hlsl @@ -238,28 +238,28 @@ struct divides< complex_t > // Out of line generic initialization of static member data not yet supported so we X-Macro identities for Scalar types we want to support // (left X-Macro here since it's pretty readable) -#define COMPLEX_ARITHMETIC_IDENTITIES(SCALAR) \ +#define COMPLEX_ARITHMETIC_IDENTITIES(SCALAR, COMPONENT) \ template<> \ -const static complex_t< SCALAR > plus< complex_t< SCALAR > >::identity = { promote< SCALAR , uint32_t>(0), promote< SCALAR , uint32_t>(0)}; \ +const static complex_t< SCALAR > plus< complex_t< SCALAR > >::identity = { promote< SCALAR, COMPONENT>(0), promote< SCALAR, COMPONENT>(0)}; \ template<> \ -const static complex_t< SCALAR > minus< complex_t< SCALAR > >::identity = { promote< SCALAR , uint32_t>(0), promote< SCALAR , uint32_t>(0)}; \ +const static complex_t< SCALAR > minus< complex_t< SCALAR > >::identity = { promote< SCALAR, COMPONENT>(0), promote< SCALAR, COMPONENT>(0)}; \ template<> \ -const static complex_t< SCALAR > multiplies< complex_t< SCALAR > >::identity = { promote< SCALAR , uint32_t>(1), promote< SCALAR , uint32_t>(0)}; \ +const static complex_t< SCALAR > multiplies< complex_t< SCALAR > >::identity = { promote< SCALAR, COMPONENT>(1), promote< SCALAR, COMPONENT>(0)}; \ template<> \ -const static complex_t< SCALAR > divides< complex_t< SCALAR > >::identity = { promote< SCALAR , uint32_t>(1), promote< SCALAR , uint32_t>(0)}; - -COMPLEX_ARITHMETIC_IDENTITIES(float16_t) -COMPLEX_ARITHMETIC_IDENTITIES(float16_t2) -COMPLEX_ARITHMETIC_IDENTITIES(float16_t3) -COMPLEX_ARITHMETIC_IDENTITIES(float16_t4) -COMPLEX_ARITHMETIC_IDENTITIES(float32_t) -COMPLEX_ARITHMETIC_IDENTITIES(float32_t2) -COMPLEX_ARITHMETIC_IDENTITIES(float32_t3) -COMPLEX_ARITHMETIC_IDENTITIES(float32_t4) -COMPLEX_ARITHMETIC_IDENTITIES(float64_t) -COMPLEX_ARITHMETIC_IDENTITIES(float64_t2) -COMPLEX_ARITHMETIC_IDENTITIES(float64_t3) -COMPLEX_ARITHMETIC_IDENTITIES(float64_t4) +const static complex_t< SCALAR > divides< complex_t< SCALAR > >::identity = { promote< SCALAR, COMPONENT>(1), promote< SCALAR, COMPONENT>(0)}; + +COMPLEX_ARITHMETIC_IDENTITIES(float16_t, float16_t) +COMPLEX_ARITHMETIC_IDENTITIES(float16_t2, float16_t) +COMPLEX_ARITHMETIC_IDENTITIES(float16_t3, float16_t) +COMPLEX_ARITHMETIC_IDENTITIES(float16_t4, float16_t) +COMPLEX_ARITHMETIC_IDENTITIES(float32_t, float32_t) +COMPLEX_ARITHMETIC_IDENTITIES(float32_t2, float32_t) +COMPLEX_ARITHMETIC_IDENTITIES(float32_t3, float32_t) +COMPLEX_ARITHMETIC_IDENTITIES(float32_t4, float32_t) +COMPLEX_ARITHMETIC_IDENTITIES(float64_t, float64_t) +COMPLEX_ARITHMETIC_IDENTITIES(float64_t2, float64_t) +COMPLEX_ARITHMETIC_IDENTITIES(float64_t3, float64_t) +COMPLEX_ARITHMETIC_IDENTITIES(float64_t4, float64_t) #undef COMPLEX_ARITHMETIC_IDENTITIES @@ -436,22 +436,6 @@ complex_t rotateRight(NBL_CONST_REF_ARG(complex_t) value) return retVal; } -template -struct ternary_operator< complex_t > -{ - using type_t = complex_t; - - complex_t operator()(bool condition, NBL_CONST_REF_ARG(complex_t) lhs, NBL_CONST_REF_ARG(complex_t) rhs) - { - const vector lhsVector = vector(lhs.real(), lhs.imag()); - const vector rhsVector = vector(rhs.real(), rhs.imag()); - const vector resultVector = condition ? lhsVector : rhsVector; - const complex_t result = { resultVector.x, resultVector.y }; - return result; - } -}; - - } } diff --git a/include/nbl/builtin/hlsl/concepts.hlsl b/include/nbl/builtin/hlsl/concepts.hlsl index 6e0f380d01..3c40b3e6c6 100644 --- a/include/nbl/builtin/hlsl/concepts.hlsl +++ b/include/nbl/builtin/hlsl/concepts.hlsl @@ -128,9 +128,9 @@ NBL_CONSTEXPR bool NBL_CONCEPT_NAME = BOOST_PP_SEQ_FOR_EACH_I(NBL_IMPL_CONCEPT_E namespace impl\ {\ template\ -struct CONCEPT_NAME : false_type {};\ +struct CONCEPT_NAME : ::nbl::hlsl::false_type {};\ template\ -struct CONCEPT_NAME > : true_type {};\ +struct CONCEPT_NAME > : ::nbl::hlsl::true_type {};\ }\ template\ NBL_BOOL_CONCEPT CONCEPT_NAME = impl::CONCEPT_NAME::value\ @@ -139,4 +139,4 @@ NBL_BOOL_CONCEPT CONCEPT_NAME = impl::CONCEPT_NAME struct is_emulating_floating_point_scalar { - NBL_CONSTEXPR_STATIC_INLINE bool value = FloatingPointScalar; + NBL_CONSTEXPR_STATIC_INLINE bool value = false; +}; + +template +struct is_emulating_integral_scalar +{ + NBL_CONSTEXPR_STATIC_INLINE bool value = false; }; } //! Floating point types are native floating point types or types that imitate native floating point types (for example emulated_float64_t) template -NBL_BOOL_CONCEPT FloatingPointLikeScalar = impl::is_emulating_floating_point_scalar::value; +NBL_BOOL_CONCEPT FloatingPointLikeScalar = FloatingPointScalar || impl::is_emulating_floating_point_scalar::value; + +//! Integral-like types are native integral types or types that imitate native integral types (for example emulated_uint64_t) +template +NBL_BOOL_CONCEPT IntegralLikeScalar = IntegralScalar || impl::is_emulating_integral_scalar::value; } } diff --git a/include/nbl/builtin/hlsl/concepts/vector.hlsl b/include/nbl/builtin/hlsl/concepts/vector.hlsl index 468838730a..f132531cb9 100644 --- a/include/nbl/builtin/hlsl/concepts/vector.hlsl +++ b/include/nbl/builtin/hlsl/concepts/vector.hlsl @@ -40,14 +40,12 @@ NBL_BOOL_CONCEPT FloatingPointLikeVectorial = concepts::Vectorial && concepts template NBL_BOOL_CONCEPT IntVectorial = concepts::Vectorial && (is_integral_v::scalar_type>); template +NBL_BOOL_CONCEPT IntegralLikeVectorial = concepts::Vectorial && concepts::IntegralLikeScalar::scalar_type>; +template NBL_BOOL_CONCEPT SignedIntVectorial = concepts::Vectorial && concepts::SignedIntegralScalar::scalar_type>; } -template -NBL_PARTIAL_REQ_TOP(concepts::Vectorial) -struct extent) > : integral_constant::Dimension> {}; - } } #endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/cpp_compat.hlsl b/include/nbl/builtin/hlsl/cpp_compat.hlsl index 175a3e76c1..03d47864fb 100644 --- a/include/nbl/builtin/hlsl/cpp_compat.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat.hlsl @@ -5,5 +5,9 @@ // it includes vector and matrix #include #include +#include + +// Had to push some stuff here to avoid circular dependencies +#include #endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/cpp_compat/basic.h b/include/nbl/builtin/hlsl/cpp_compat/basic.h index 87baa1f0d6..89753d4ba5 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/basic.h +++ b/include/nbl/builtin/hlsl/cpp_compat/basic.h @@ -3,39 +3,12 @@ #include -namespace nbl -{ -namespace hlsl -{ -namespace impl -{ -template -struct static_cast_helper -{ - static inline To cast(From u) - { -#ifndef __HLSL_VERSION - return static_cast(u); -#else - return To(u); -#endif - } -}; -} - -template -inline To _static_cast(From v) -{ - return impl::static_cast_helper::cast(v); -} - -} -} #ifndef __HLSL_VERSION #include #define ARROW -> +#define NBL_DEREF_THIS (*this) #define NBL_CONSTEXPR constexpr // TODO: rename to NBL_CONSTEXPR_VAR #define NBL_CONSTEXPR_FUNC constexpr #define NBL_CONSTEXPR_STATIC constexpr static @@ -43,6 +16,10 @@ inline To _static_cast(From v) #define NBL_CONSTEXPR_INLINE_FUNC constexpr inline #define NBL_CONSTEXPR_FORCED_INLINE_FUNC NBL_FORCE_INLINE constexpr #define NBL_CONST_MEMBER_FUNC const +#define NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR constexpr inline +#define NBL_CONSTEXPR_FUNC_SCOPE_VAR constexpr +#define NBL_CONSTEXPR_OOL_MEMBER constexpr +#define NBL_CONSTEXPR_INLINE_OOL_MEMBER constexpr inline #define NBL_IF_CONSTEXPR(...) if constexpr (__VA_ARGS__) namespace nbl::hlsl @@ -67,6 +44,7 @@ namespace nbl::hlsl #else #define ARROW .arrow(). +#define NBL_DEREF_THIS this #define NBL_CONSTEXPR const static // TODO: rename to NBL_CONSTEXPR_VAR #define NBL_CONSTEXPR_FUNC #define NBL_CONSTEXPR_STATIC const static @@ -74,6 +52,10 @@ namespace nbl::hlsl #define NBL_CONSTEXPR_INLINE_FUNC inline #define NBL_CONSTEXPR_FORCED_INLINE_FUNC inline #define NBL_CONST_MEMBER_FUNC +#define NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR const static +#define NBL_CONSTEXPR_FUNC_SCOPE_VAR const +#define NBL_CONSTEXPR_OOL_MEMBER const +#define NBL_CONSTEXPR_INLINE_OOL_MEMBER const #define NBL_IF_CONSTEXPR(...) if (__VA_ARGS__) namespace nbl @@ -102,4 +84,35 @@ struct add_pointer #endif + + +namespace nbl +{ +namespace hlsl +{ +namespace impl +{ +template +struct static_cast_helper +{ + NBL_CONSTEXPR_STATIC_INLINE To cast(From u) + { +#ifndef __HLSL_VERSION + return static_cast(u); +#else + return To(u); +#endif + } +}; +} + +template +NBL_CONSTEXPR_INLINE_FUNC To _static_cast(From v) +{ + return impl::static_cast_helper::cast(v); +} + +} +} + #endif diff --git a/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl b/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl index 0c595bb0e2..cbaa0a7b7b 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl @@ -27,7 +27,7 @@ template::type; - NBL_CONSTEXPR UnsignedInteger Mask = (UnsignedInteger(0) - 1) >> 1; + NBL_CONSTEXPR_FUNC_SCOPE_VAR UnsignedInteger Mask = (UnsignedInteger(0) - 1) >> 1; UnsignedInteger absVal = val & Mask; return absVal > (ieee754::traits::specialValueExp << ieee754::traits::mantissaBitCnt); } @@ -48,7 +48,7 @@ NBL_VALID_EXPRESSION(MixIsCallable, (T)(U), glm::mix(declval(),declval(),d template NBL_BOOL_CONCEPT MixCallingBuiltins = #ifdef __HLSL_VERSION -(spirv::FMixIsCallable && is_same_v) || spirv::SelectIsCallable; +(spirv::FMixIsCallable && is_same_v); #else MixIsCallable; #endif @@ -90,6 +90,8 @@ template struct all_helper; template struct any_helper; +template +struct select_helper; template struct bitReverseAs_helper; template @@ -119,6 +121,12 @@ struct nMax_helper; template struct nClamp_helper; template +struct addCarry_helper; +template +struct subBorrow_helper; +template +struct undef_helper; +template struct fma_helper; #ifdef __HLSL_VERSION // HLSL only specializations @@ -133,8 +141,8 @@ struct fma_helper; // the template<> needs to be written ourselves // return type is __VA_ARGS__ to protect against `,` in templated return types #define AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(HELPER_NAME, SPIRV_FUNCTION_NAME, ARG_TYPE_LIST, ARG_TYPE_SET, ...)\ -NBL_PARTIAL_REQ_TOP(is_same_v(BOOST_PP_SEQ_FOR_EACH_I(DECLVAL, _, ARG_TYPE_SET))), __VA_ARGS__ >) \ -struct HELPER_NAME(BOOST_PP_SEQ_FOR_EACH_I(DECLVAL, _, ARG_TYPE_SET))), __VA_ARGS__ >) >\ +NBL_PARTIAL_REQ_TOP(is_same_v(BOOST_PP_SEQ_FOR_EACH_I(DECLVAL, _, ARG_TYPE_SET))), __VA_ARGS__ >) \ +struct HELPER_NAME(BOOST_PP_SEQ_FOR_EACH_I(DECLVAL, _, ARG_TYPE_SET))), __VA_ARGS__ >) >\ {\ using return_t = __VA_ARGS__;\ static inline return_t __call( BOOST_PP_SEQ_FOR_EACH_I(DECL_ARG, _, ARG_TYPE_SET) )\ @@ -156,8 +164,9 @@ template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(length_helper, length, template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(normalize_helper, normalize, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(rsqrt_helper, inverseSqrt, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(fract_helper, fract, (T), (T), T) -template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(all_helper, any, (T), (T), bool) +template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(all_helper, all, (T), (T), bool) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(any_helper, any, (T), (T), bool) +template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(select_helper, select, (T)(B), (B)(T)(T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(sign_helper, fSign, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(sign_helper, sSign, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(radians_helper, radians, (T), (T), T) @@ -179,6 +188,10 @@ template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(refract_hel template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(nMax_helper, nMax, (T), (T)(T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(nMin_helper, nMin, (T), (T)(T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(nClamp_helper, nClamp, (T), (T)(T), T) +// Can use trivial case and not worry about restricting `T` with a concept since `spirv::AddCarryOutput / SubBorrowOutput` already take care of that +template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(addCarry_helper, addCarry, (T), (T)(T), spirv::AddCarryOutput) +template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(subBorrow_helper, subBorrow, (T), (T)(T), spirv::SubBorrowOutput) +template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(undef_helper, undef, (T), , T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(fma_helper, fma, (T), (T)(T)(T), T) #define BITCOUNT_HELPER_RETRUN_TYPE conditional_t, vector::Dimension>, int32_t> @@ -255,20 +268,6 @@ struct mix_helper) > } }; -template -NBL_PARTIAL_REQ_TOP(spirv::SelectIsCallable && concepts::Boolean) -struct mix_helper && concepts::Boolean) > -{ - using return_t = conditional_t, vector::scalar_type, vector_traits::Dimension>, T>; - // for a component of a that is false, the corresponding component of x is returned - // for a component of a that is true, the corresponding component of y is returned - // so we make sure this is correct when calling the operation - static inline return_t __call(const T x, const T y, const U a) - { - return spirv::select(a, y, x); - } -}; - template NBL_PARTIAL_REQ_TOP(matrix_traits::Square) struct determinant_helper::Square) > { @@ -629,6 +628,74 @@ struct nClamp_helper } }; +// Once again no need to restrict the two below with concepts for same reason as HLSL version +template +struct addCarry_helper +{ + using return_t = spirv::AddCarryOutput; + constexpr static inline return_t __call(const T operand1, const T operand2) + { + return_t retVal; + retVal.result = operand1 + operand2; + retVal.carry = T(retVal.result < operand1); + return retVal; + } +}; + +template +struct subBorrow_helper +{ + using return_t = spirv::SubBorrowOutput; + constexpr static inline return_t __call(const T operand1, const T operand2) + { + return_t retVal; + retVal.result = static_cast(operand1 - operand2); + retVal.borrow = T(operand1 < operand2); + return retVal; + } +}; + +template +requires (concepts::BooleanScalar) +struct select_helper +{ + using return_t = T; + constexpr static return_t __call(const B& condition, const T& object1, const T& object2) + { + return condition ? object1 : object2; + } +}; + +template +requires (concepts::Boolean&& concepts::Vector&& concepts::Vector && (extent_v == extent_v)) +struct select_helper +{ + using return_t = T; + constexpr static T __call(const B& condition, const T& object1, const T& object2) + { + using traits = vector_traits; + array_get conditionGetter; + array_get objectGetter; + array_set setter; + + T selected; + for (uint32_t i = 0; i < traits::Dimension; ++i) + setter(selected, i, conditionGetter(condition, i) ? objectGetter(object1, i) : objectGetter(object2, i)); + + return selected; + } +}; + +template +struct undef_helper +{ + NBL_CONSTEXPR_STATIC T __call() + { + T t; + return t; + } +}; + template requires concepts::FloatingPointScalar struct fma_helper @@ -772,6 +839,17 @@ struct mul_helper && conce } }; +template +NBL_PARTIAL_REQ_TOP((concepts::Matrix && concepts::Scalar) || (concepts::Scalar && concepts::Matrix)) +struct mul_helper && concepts::Scalar) || (concepts::Scalar && concepts::Matrix)) > +{ + using return_t = hlsl::conditional_t, LhsT, RhsT>; + static inline return_t __call(LhsT lhs, RhsT rhs) + { + return mul(lhs, rhs); + } +}; + #define AUTO_SPECIALIZE_HELPER_FOR_VECTOR(HELPER_NAME, REQUIREMENT, RETURN_TYPE)\ template\ NBL_PARTIAL_REQ_TOP(REQUIREMENT)\ @@ -901,43 +979,24 @@ struct mix_helper -NBL_PARTIAL_REQ_TOP(VECTOR_SPECIALIZATION_CONCEPT && !impl::MixCallingBuiltins && concepts::BooleanScalar) -struct mix_helper && concepts::BooleanScalar) > +namespace impl { - using return_t = T; - static return_t __call(NBL_CONST_REF_ARG(T) x, NBL_CONST_REF_ARG(T) y, NBL_CONST_REF_ARG(U) a) - { - using traitsT = hlsl::vector_traits; - array_get getterT; - array_set setter; - - return_t output; - for (uint32_t i = 0; i < traitsT::Dimension; ++i) - setter(output, i, mix_helper::__call(getterT(x, i), getterT(y, i), a)); - - return output; - } -}; - template -NBL_PARTIAL_REQ_TOP(VECTOR_SPECIALIZATION_CONCEPT && !impl::MixCallingBuiltins && concepts::Boolean && concepts::Vectorial && vector_traits::Dimension == vector_traits::Dimension) -struct mix_helper && concepts::Boolean && concepts::Vectorial && vector_traits::Dimension == vector_traits::Dimension) > +NBL_BOOL_CONCEPT MixCallingSelect = +#ifdef __HLSL_VERSION +spirv::SelectIsCallable; +#else +concepts::Boolean && (concepts::Scalar || (concepts::Vector && vector_traits::Dimension==vector_traits::Dimension)) && !MixCallingBuiltins; +#endif +} + +template NBL_PARTIAL_REQ_TOP(impl::MixCallingSelect) +struct mix_helper) > { using return_t = T; static return_t __call(NBL_CONST_REF_ARG(T) x, NBL_CONST_REF_ARG(T) y, NBL_CONST_REF_ARG(U) a) { - using traitsT = hlsl::vector_traits; - using traitsU = hlsl::vector_traits; - array_get getterT; - array_get getterU; - array_set setter; - - return_t output; - for (uint32_t i = 0; i < traitsT::Dimension; ++i) - setter(output, i, mix_helper::__call(getterT(x, i), getterT(y, i), getterU(a, i))); - - return output; + return select_helper::__call(a, y, x); } }; diff --git a/include/nbl/builtin/hlsl/cpp_compat/intrinsics.hlsl b/include/nbl/builtin/hlsl/cpp_compat/intrinsics.hlsl index 7198bae563..78367f7924 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/intrinsics.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/intrinsics.hlsl @@ -23,6 +23,12 @@ namespace nbl namespace hlsl { +template +NBL_CONSTEXPR_FUNC T undef() +{ + return cpp_compat_intrinsics_impl::undef_helper::__call(); +} + template inline typename cpp_compat_intrinsics_impl::bitCount_helper::return_t bitCount(NBL_CONST_REF_ARG(T) val) { @@ -150,6 +156,12 @@ inline bool any(Vector vec) return cpp_compat_intrinsics_impl::any_helper::__call(vec); } +template +NBL_CONSTEXPR_FUNC ResultType select(Condition condition, ResultType object1, ResultType object2) +{ + return cpp_compat_intrinsics_impl::select_helper::__call(condition, object1, object2); +} + /** * @brief Returns x - floor(x). * @@ -217,6 +229,19 @@ inline T refract(NBL_CONST_REF_ARG(T) I, NBL_CONST_REF_ARG(T) N, NBL_CONST_REF_A return cpp_compat_intrinsics_impl::refract_helper::__call(I, N, eta); } +template +NBL_CONSTEXPR_FUNC spirv::AddCarryOutput addCarry(NBL_CONST_REF_ARG(T) operand1, NBL_CONST_REF_ARG(T) operand2) +{ + return cpp_compat_intrinsics_impl::addCarry_helper::__call(operand1, operand2); +} + +template +NBL_CONSTEXPR_FUNC spirv::SubBorrowOutput subBorrow(NBL_CONST_REF_ARG(T) operand1, NBL_CONST_REF_ARG(T) operand2) +{ + return cpp_compat_intrinsics_impl::subBorrow_helper::__call(operand1, operand2); +} + + #ifdef __HLSL_VERSION #define NAMESPACE spirv #else diff --git a/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl b/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl index 1ee5edf275..5be66ebae7 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/matrix.hlsl @@ -28,8 +28,15 @@ struct matrix final : private glm::mat return *this; } - friend matrix operator+(matrix const& lhs, matrix const& rhs){ return matrix(reinterpret_cast(lhs) + reinterpret_cast(rhs)); } - friend matrix operator-(matrix const& lhs, matrix const& rhs){ return matrix(reinterpret_cast(lhs) - reinterpret_cast(rhs)); } + // not sure how to forward this + //inline friend matrix operator*(matrix const& lhs, T rhs) {return matrix(reinterpret_cast(lhs)*rhs);} + + // scalar compound assignment multiply and divide + inline matrix& operator*=(const T rhs) {return reinterpret_cast(Base::template operator*=(rhs));} + inline matrix& operator/=(const T rhs) {return reinterpret_cast(Base::template operator/=(rhs));} + + inline friend matrix operator+(matrix const& lhs, matrix const& rhs){ return matrix(reinterpret_cast(lhs) + reinterpret_cast(rhs)); } + inline friend matrix operator-(matrix const& lhs, matrix const& rhs){ return matrix(reinterpret_cast(lhs) - reinterpret_cast(rhs)); } template inline friend matrix mul(matrix const& lhs, matrix const& rhs) @@ -44,6 +51,14 @@ struct matrix final : private glm::mat { return glm::operator*(reinterpret_cast(rhs), lhs); } + inline friend bool operator==(matrix const& lhs, matrix const& rhs) + { + return glm::operator==(reinterpret_cast(lhs), reinterpret_cast(rhs)); + } + inline friend bool operator!=(matrix const& lhs, matrix const& rhs) + { + return glm::operator!=(reinterpret_cast(lhs), reinterpret_cast(rhs)); + } }; #endif diff --git a/include/nbl/builtin/hlsl/cpp_compat/promote.hlsl b/include/nbl/builtin/hlsl/cpp_compat/promote.hlsl index 51ca73f6d3..1887f4b51f 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/promote.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/promote.hlsl @@ -12,68 +12,39 @@ namespace impl { // partial specialize this for `T=matrix|vector` and `U=matrix|vector|scalar_t` -template +template struct Promote { - T operator()(U v) + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(U) v) { return T(v); } }; -#ifdef __HLSL_VERSION - -template -struct Promote, U> -{ - enable_if_t::value && is_scalar::value, vector > operator()(U v) - { - vector promoted = {Scalar(v)}; - return promoted; - } -}; - -template -struct Promote, U> +template NBL_PARTIAL_REQ_TOP(concepts::Vectorial && (concepts::IntegralLikeScalar || concepts::FloatingPointLikeScalar) && is_same_v::scalar_type, From>) +struct Promote && (concepts::IntegralLikeScalar || concepts::FloatingPointLikeScalar) && is_same_v::scalar_type, From>) > { - enable_if_t::value && is_scalar::value, vector > operator()(U v) + NBL_CONSTEXPR_FUNC To operator()(const From v) { - vector promoted = {Scalar(v), Scalar(v)}; - return promoted; + array_set setter; + To output; + [[unroll]] + for (int i = 0; i < vector_traits::Dimension; ++i) + setter(output, i, v); + return output; } }; -template -struct Promote, U> -{ - enable_if_t::value && is_scalar::value, vector > operator()(U v) - { - vector promoted = {Scalar(v), Scalar(v), Scalar(v)}; - return promoted; - } -}; - -template -struct Promote, U> -{ - enable_if_t::value && is_scalar::value, vector > operator()(U v) - { - vector promoted = {Scalar(v), Scalar(v), Scalar(v), Scalar(v)}; - return promoted; - } -}; - -#endif - } template -T promote(const U v) // TODO: use NBL_CONST_REF_ARG(U) instead of U v (circular ref) +NBL_CONSTEXPR_FUNC T promote(NBL_CONST_REF_ARG(U) v) { impl::Promote _promote; return _promote(v); } + } } diff --git a/include/nbl/builtin/hlsl/cpp_compat/truncate.hlsl b/include/nbl/builtin/hlsl/cpp_compat/truncate.hlsl new file mode 100644 index 0000000000..ffe3d12641 --- /dev/null +++ b/include/nbl/builtin/hlsl/cpp_compat/truncate.hlsl @@ -0,0 +1,58 @@ +#ifndef _NBL_BUILTIN_HLSL_CPP_COMPAT_TRUNCATE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_CPP_COMPAT_TRUNCATE_INCLUDED_ + +#include "nbl/builtin/hlsl/type_traits.hlsl" +#include "nbl/builtin/hlsl/concepts/core.hlsl" + +namespace nbl +{ +namespace hlsl +{ + +namespace concepts +{ + template + NBL_BOOL_CONCEPT can_truncate_vector = concepts::Vectorial && concepts::Vectorial && concepts::same_as::scalar_type, typename vector_traits::scalar_type > && vector_traits::Dimension <= vector_traits::Dimension; +} + +namespace impl +{ + +template +struct Truncate +{ + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(U) v) + { + return T(v); + } +}; + +template NBL_PARTIAL_REQ_TOP(concepts::can_truncate_vector) +struct Truncate) > +{ + NBL_CONSTEXPR_FUNC To operator()(const From v) + { + array_get::scalar_type> getter; + array_set::scalar_type> setter; + To output; + [[unroll]] + for (int i = 0; i < vector_traits::Dimension; ++i) + setter(output, i, getter(v, i)); + return output; + } + +}; + +} //namespace impl + +template +NBL_CONSTEXPR_FUNC T truncate(NBL_CONST_REF_ARG(U) v) +{ + impl::Truncate _truncate; + return _truncate(v); +} + +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/emulated/float64_t.hlsl b/include/nbl/builtin/hlsl/emulated/float64_t.hlsl index 9872675e3a..da32fab7b0 100644 --- a/include/nbl/builtin/hlsl/emulated/float64_t.hlsl +++ b/include/nbl/builtin/hlsl/emulated/float64_t.hlsl @@ -471,25 +471,25 @@ inline int extractExponent(__VA_ARGS__ x)\ }\ \ template<>\ -NBL_CONSTEXPR_INLINE_FUNC __VA_ARGS__ replaceBiasedExponent(__VA_ARGS__ x, typename unsigned_integer_of_size::type biasedExp)\ +NBL_CONSTEXPR_FUNC __VA_ARGS__ replaceBiasedExponent(__VA_ARGS__ x, typename unsigned_integer_of_size::type biasedExp)\ {\ return __VA_ARGS__(replaceBiasedExponent(x.data, biasedExp));\ }\ \ template <>\ -NBL_CONSTEXPR_INLINE_FUNC __VA_ARGS__ fastMulExp2(__VA_ARGS__ x, int n)\ +NBL_CONSTEXPR_FUNC __VA_ARGS__ fastMulExp2(__VA_ARGS__ x, int n)\ {\ return __VA_ARGS__(replaceBiasedExponent(x.data, extractBiasedExponent(x) + uint32_t(n)));\ }\ \ template <>\ -NBL_CONSTEXPR_INLINE_FUNC unsigned_integer_of_size::type extractMantissa(__VA_ARGS__ x)\ +NBL_CONSTEXPR_FUNC unsigned_integer_of_size::type extractMantissa(__VA_ARGS__ x)\ {\ return extractMantissa(x.data);\ }\ \ template <>\ -NBL_CONSTEXPR_INLINE_FUNC uint64_t extractNormalizeMantissa(__VA_ARGS__ x)\ +NBL_CONSTEXPR_FUNC uint64_t extractNormalizeMantissa(__VA_ARGS__ x)\ {\ return extractNormalizeMantissa(x.data);\ }\ @@ -636,10 +636,10 @@ namespace ieee754 { namespace impl { -template<> NBL_CONSTEXPR_INLINE_FUNC uint64_t bitCastToUintType(emulated_float64_t x) { return x.data; } -template<> NBL_CONSTEXPR_INLINE_FUNC uint64_t bitCastToUintType(emulated_float64_t x) { return x.data; } -template<> NBL_CONSTEXPR_INLINE_FUNC uint64_t bitCastToUintType(emulated_float64_t x) { return x.data; } -template<> NBL_CONSTEXPR_INLINE_FUNC uint64_t bitCastToUintType(emulated_float64_t x) { return x.data; } +template<> NBL_CONSTEXPR_FUNC uint64_t bitCastToUintType(emulated_float64_t x) { return x.data; } +template<> NBL_CONSTEXPR_FUNC uint64_t bitCastToUintType(emulated_float64_t x) { return x.data; } +template<> NBL_CONSTEXPR_FUNC uint64_t bitCastToUintType(emulated_float64_t x) { return x.data; } +template<> NBL_CONSTEXPR_FUNC uint64_t bitCastToUintType(emulated_float64_t x) { return x.data; } } IMPLEMENT_IEEE754_FUNC_SPEC_FOR_EMULATED_F64_TYPE(emulated_float64_t); diff --git a/include/nbl/builtin/hlsl/emulated/float64_t_impl.hlsl b/include/nbl/builtin/hlsl/emulated/float64_t_impl.hlsl index 44b881345d..df785e3e8f 100644 --- a/include/nbl/builtin/hlsl/emulated/float64_t_impl.hlsl +++ b/include/nbl/builtin/hlsl/emulated/float64_t_impl.hlsl @@ -41,7 +41,7 @@ namespace hlsl { namespace emulated_float64_t_impl { -NBL_CONSTEXPR_INLINE_FUNC uint64_t2 shiftMantissaLeftBy53(uint64_t mantissa64) +NBL_CONSTEXPR_FUNC uint64_t2 shiftMantissaLeftBy53(uint64_t mantissa64) { uint64_t2 output; output.x = mantissa64 >> (64 - ieee754::traits::mantissaBitCnt); @@ -74,7 +74,7 @@ inline uint64_t castFloat32ToStorageType(float32_t val) } }; -NBL_CONSTEXPR_INLINE_FUNC bool isZero(uint64_t val) +NBL_CONSTEXPR_FUNC bool isZero(uint64_t val) { return (val << 1) == 0ull; } @@ -137,18 +137,18 @@ inline uint64_t reinterpretAsFloat64BitPattern(int64_t val) return sign | reinterpretAsFloat64BitPattern(absVal); }; -NBL_CONSTEXPR_INLINE_FUNC uint64_t flushDenormToZero(uint64_t value) +NBL_CONSTEXPR_FUNC uint64_t flushDenormToZero(uint64_t value) { const uint64_t biasBits = value & ieee754::traits::exponentMask; return biasBits ? value : (value & ieee754::traits::signMask); } -NBL_CONSTEXPR_INLINE_FUNC uint64_t assembleFloat64(uint64_t signShifted, uint64_t expShifted, uint64_t mantissa) +NBL_CONSTEXPR_FUNC uint64_t assembleFloat64(uint64_t signShifted, uint64_t expShifted, uint64_t mantissa) { return signShifted | expShifted | mantissa; } -NBL_CONSTEXPR_INLINE_FUNC bool areBothInfinity(uint64_t lhs, uint64_t rhs) +NBL_CONSTEXPR_FUNC bool areBothInfinity(uint64_t lhs, uint64_t rhs) { lhs &= ~ieee754::traits::signMask; rhs &= ~ieee754::traits::signMask; @@ -156,18 +156,18 @@ NBL_CONSTEXPR_INLINE_FUNC bool areBothInfinity(uint64_t lhs, uint64_t rhs) return lhs == rhs && lhs == ieee754::traits::inf; } -NBL_CONSTEXPR_INLINE_FUNC bool areBothZero(uint64_t lhs, uint64_t rhs) +NBL_CONSTEXPR_FUNC bool areBothZero(uint64_t lhs, uint64_t rhs) { return !bool((lhs | rhs) << 1); } -NBL_CONSTEXPR_INLINE_FUNC bool areBothSameSignZero(uint64_t lhs, uint64_t rhs) +NBL_CONSTEXPR_FUNC bool areBothSameSignZero(uint64_t lhs, uint64_t rhs) { return !bool((lhs) << 1) && (lhs == rhs); } template -NBL_CONSTEXPR_INLINE_FUNC bool operatorLessAndGreaterCommonImplementation(uint64_t lhs, uint64_t rhs) +NBL_CONSTEXPR_FUNC bool operatorLessAndGreaterCommonImplementation(uint64_t lhs, uint64_t rhs) { if (!FastMath) { diff --git a/include/nbl/builtin/hlsl/emulated/int64_common_member_inc.hlsl b/include/nbl/builtin/hlsl/emulated/int64_common_member_inc.hlsl new file mode 100644 index 0000000000..3818814a49 --- /dev/null +++ b/include/nbl/builtin/hlsl/emulated/int64_common_member_inc.hlsl @@ -0,0 +1,155 @@ +using storage_t = vector; +storage_t data; + +/** +* @brief Creates an `emulated_int64` from a vector of two `uint32_t`s representing its bitpattern +* +* @param [in] _data Vector of `uint32_t` encoding the `uint64_t/int64_t` being emulated. Stored as little endian (first component are the lower 32 bits) +*/ +NBL_CONSTEXPR_STATIC this_t create(NBL_CONST_REF_ARG(storage_t) _data) +{ + this_t retVal; + retVal.data = _data; + return retVal; +} + +/** +* @brief Creates an `emulated_int64` from two `uint32_t`s representing its bitpattern +* +* @param [in] lo Lowest 32 bits of the `uint64_t/int64_t` being emulated +* @param [in] hi Highest 32 bits of the `uint64_t/int64_t` being emulated +*/ +NBL_CONSTEXPR_STATIC this_t create(NBL_CONST_REF_ARG(uint32_t) lo, NBL_CONST_REF_ARG(uint32_t) hi) +{ + return create(storage_t(lo, hi)); +} + +// ------------------------------------------------------- CONVERSION OPERATORS--------------------------------------------------------------- +// GLM requires these for vector casts + +#ifndef __HLSL_VERSION + +template +constexpr explicit operator I() const noexcept; + +#endif + +// ------------------------------------------------------- INTERNAL GETTERS ------------------------------------------------- + +NBL_CONSTEXPR_FUNC uint32_t __getLSB() NBL_CONST_MEMBER_FUNC +{ + return data.x; +} + +NBL_CONSTEXPR_FUNC uint32_t __getMSB() NBL_CONST_MEMBER_FUNC +{ + return data.y; +} + +// ------------------------------------------------------- BITWISE OPERATORS ------------------------------------------------- + +NBL_CONSTEXPR_FUNC this_t operator&(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + this_t retVal = create(data & rhs.data); + return retVal; +} + +NBL_CONSTEXPR_FUNC this_t operator|(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + this_t retVal = create(data | rhs.data); + return retVal; +} + +NBL_CONSTEXPR_FUNC this_t operator^(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + this_t retVal = create(data ^ rhs.data); + return retVal; +} + +NBL_CONSTEXPR_FUNC this_t operator~() NBL_CONST_MEMBER_FUNC +{ + this_t retVal = create(~data); + return retVal; +} + +// Only valid in CPP +#ifndef __HLSL_VERSION +constexpr inline this_t operator>>(uint32_t bits) const; + +constexpr inline this_t operator<<(uint32_t bits) const; + +constexpr inline this_t& operator&=(const this_t& val) +{ + data &= val.data; + return *this; +} + +constexpr inline this_t& operator|=(const this_t& val) +{ + data |= val.data; + return *this; +} + +constexpr inline this_t& operator^=(const this_t& val) +{ + data ^= val.data; + return *this; +} + +#endif + +// ------------------------------------------------------- ARITHMETIC OPERATORS ------------------------------------------------- + +NBL_CONSTEXPR_FUNC this_t operator+(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + const spirv::AddCarryOutput lowerAddResult = addCarry(__getLSB(), rhs.__getLSB()); + return create(lowerAddResult.result, __getMSB() + rhs.__getMSB() + lowerAddResult.carry); +} + +NBL_CONSTEXPR_FUNC this_t operator-(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + const spirv::SubBorrowOutput lowerSubResult = subBorrow(__getLSB(), rhs.__getLSB()); + return create(lowerSubResult.result, __getMSB() - rhs.__getMSB() - lowerSubResult.borrow); +} + +// ------------------------------------------------------- COMPARISON OPERATORS ------------------------------------------------- +NBL_CONSTEXPR_FUNC bool operator==(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + equal_to equals; + return all(equals(data, rhs.data)); +} + +NBL_CONSTEXPR_FUNC bool operator!=(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + not_equal_to notEquals; + return any(notEquals(data, rhs.data)); +} + +NBL_CONSTEXPR_FUNC bool operator<(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + // Either the topmost bits, when interpreted with correct sign, are less than those of `rhs`, or they're equal and the lower bits are less + // (lower bits are always positive in both unsigned and 2's complement so comparison can happen as-is) + const bool MSBEqual = __getMSB() == rhs.__getMSB(); + const bool MSB = Signed ? (bit_cast(__getMSB()) < bit_cast(rhs.__getMSB())) : (__getMSB() < rhs.__getMSB()); + const bool LSB = __getLSB() < rhs.__getLSB(); + return MSBEqual ? LSB : MSB; +} + +NBL_CONSTEXPR_FUNC bool operator>(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + // Same reasoning as above + const bool MSBEqual = __getMSB() == rhs.__getMSB(); + const bool MSB = Signed ? (bit_cast(__getMSB()) > bit_cast(rhs.__getMSB())) : (__getMSB() > rhs.__getMSB()); + const bool LSB = __getLSB() > rhs.__getLSB(); + return MSBEqual ? LSB : MSB; +} + +NBL_CONSTEXPR_FUNC bool operator<=(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + return !operator>(rhs); +} + +NBL_CONSTEXPR_FUNC bool operator>=(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC +{ + return !operator<(rhs); +} diff --git a/include/nbl/builtin/hlsl/emulated/int64_t.hlsl b/include/nbl/builtin/hlsl/emulated/int64_t.hlsl new file mode 100644 index 0000000000..4fa2014607 --- /dev/null +++ b/include/nbl/builtin/hlsl/emulated/int64_t.hlsl @@ -0,0 +1,409 @@ +#ifndef _NBL_BUILTIN_HLSL_EMULATED_INT64_T_HLSL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_EMULATED_INT64_T_HLSL_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/functional.hlsl" +#include "nbl/builtin/hlsl/concepts/core.hlsl" +#include "nbl/builtin/hlsl/bit.hlsl" + +// Didn't bother with operator*, operator/, implement if you need them. Multiplication is pretty straightforward, division requires switching on signs +// and whether the topmost bits of the divisor are equal to 0 +// - Francisco + +namespace nbl +{ +namespace hlsl +{ + +struct emulated_int64_t; + +struct emulated_uint64_t +{ + using this_t = emulated_uint64_t; + NBL_CONSTEXPR_STATIC_INLINE bool Signed = false; + + #include "int64_common_member_inc.hlsl" + + #ifndef __HLSL_VERSION + emulated_uint64_t() = default; + // GLM requires these to cast vectors because it uses a native `static_cast` + template + constexpr explicit emulated_uint64_t(const I& toEmulate); + + constexpr explicit emulated_uint64_t(const emulated_int64_t& other); + #endif +}; + + +struct emulated_int64_t +{ + using this_t = emulated_int64_t; + NBL_CONSTEXPR_STATIC_INLINE bool Signed = true; + + #include "int64_common_member_inc.hlsl" + + #ifndef __HLSL_VERSION + emulated_int64_t() = default; + // GLM requires these to cast vectors because it uses a native `static_cast` + template + constexpr explicit emulated_int64_t(const I& toEmulate); + + constexpr explicit emulated_int64_t(const emulated_uint64_t& other); + #endif + + NBL_CONSTEXPR_FUNC emulated_int64_t operator-() NBL_CONST_MEMBER_FUNC + { + storage_t inverted = ~data; + return create(_static_cast(inverted)) + _static_cast(1); + } + +}; + +// ------------------------------------------------ TYPE TRAITS SATISFIED ----------------------------------------------------- + +template<> +struct is_signed : bool_constant {}; + +template<> +struct is_unsigned : bool_constant {}; + +// --------------------------------------------------- CONCEPTS SATISFIED ----------------------------------------------------- +namespace concepts +{ + +template +NBL_BOOL_CONCEPT EmulatedIntegralScalar64 = same_as || same_as; + +namespace impl +{ + +template<> +struct is_emulating_integral_scalar +{ + NBL_CONSTEXPR_STATIC_INLINE bool value = true; +}; + +template<> +struct is_emulating_integral_scalar +{ + NBL_CONSTEXPR_STATIC_INLINE bool value = true; +}; +} + + +} + + +namespace impl +{ + +template NBL_PARTIAL_REQ_TOP(concepts::EmulatedIntegralScalar64 && concepts::EmulatedIntegralScalar64 && !concepts::same_as) +struct static_cast_helper && concepts::EmulatedIntegralScalar64 && !concepts::same_as) > +{ + + NBL_CONSTEXPR_STATIC To cast(NBL_CONST_REF_ARG(From) other) + { + To retVal; + retVal.data = other.data; + return retVal; + } +}; + +template NBL_PARTIAL_REQ_TOP(concepts::IntegralScalar && (sizeof(To) <= sizeof(uint32_t)) && concepts::EmulatedIntegralScalar64) +struct static_cast_helper && (sizeof(To) <= sizeof(uint32_t)) && concepts::EmulatedIntegralScalar64) > +{ + // Return only the lowest bits + NBL_CONSTEXPR_STATIC To cast(NBL_CONST_REF_ARG(From) val) + { + return _static_cast(val.data.x); + } +}; + +template NBL_PARTIAL_REQ_TOP(concepts::IntegralScalar && (sizeof(To) > sizeof(uint32_t)) && concepts::EmulatedIntegralScalar64) +struct static_cast_helper && (sizeof(To) > sizeof(uint32_t)) && concepts::EmulatedIntegralScalar64) > +{ + NBL_CONSTEXPR_STATIC To cast(NBL_CONST_REF_ARG(From) val) + { + return bit_cast(val.data); + } +}; + +template NBL_PARTIAL_REQ_TOP(concepts::IntegralScalar && (sizeof(From) <= sizeof(uint32_t)) && concepts::EmulatedIntegralScalar64) +struct static_cast_helper && (sizeof(From) <= sizeof(uint32_t)) && concepts::EmulatedIntegralScalar64) > +{ + // Set only lower bits + NBL_CONSTEXPR_STATIC To cast(NBL_CONST_REF_ARG(From) i) + { + return To::create(_static_cast(i), uint32_t(0)); + } +}; + +template NBL_PARTIAL_REQ_TOP(concepts::IntegralScalar && (sizeof(From) > sizeof(uint32_t)) && concepts::EmulatedIntegralScalar64) +struct static_cast_helper && (sizeof(From) > sizeof(uint32_t)) && concepts::EmulatedIntegralScalar64) > +{ + NBL_CONSTEXPR_STATIC To cast(NBL_CONST_REF_ARG(From) i) + { + // `bit_cast` blocked by GLM vectors using a union + #ifndef __HLSL_VERSION + return To::create(_static_cast(i), _static_cast(i >> 32)); + #else + To retVal; + retVal.data = bit_cast >(i); + return retVal; + #endif + } +}; + +} //namespace impl + +// Define constructor and conversion operators + +#ifndef __HLSL_VERSION + +constexpr emulated_int64_t::emulated_int64_t(const emulated_uint64_t& other) : data(other.data) {} + +constexpr emulated_uint64_t::emulated_uint64_t(const emulated_int64_t& other) : data(other.data) {} + +template +constexpr emulated_int64_t::emulated_int64_t(const I& toEmulate) +{ + *this = _static_cast(toEmulate); +} + +template +constexpr emulated_uint64_t::emulated_uint64_t(const I& toEmulate) +{ + *this = _static_cast(toEmulate); +} + +template +constexpr emulated_int64_t::operator I() const noexcept +{ + return _static_cast(*this); +} + +template +constexpr emulated_uint64_t::operator I() const noexcept +{ + return _static_cast(*this); +} + +#endif + +// ---------------------- Functional operators ------------------------ + +template NBL_PARTIAL_REQ_TOP(concepts::EmulatedIntegralScalar64) +struct left_shift_operator) > +{ + using type_t = T; + NBL_CONSTEXPR_STATIC uint32_t ComponentBitWidth = uint32_t(8 * sizeof(uint32_t)); + + // Can't do generic templated definition, see: + //https://github.com/microsoft/DirectXShaderCompiler/issues/7325 + + // If `_bits > 63` or `_bits < 0` the result is undefined + NBL_CONSTEXPR_FUNC type_t operator()(NBL_CONST_REF_ARG(type_t) operand, uint32_t bits) + { + const bool bigShift = bits >= ComponentBitWidth; // Shift that completely rewrites LSB + const uint32_t shift = bigShift ? bits - ComponentBitWidth : ComponentBitWidth - bits; + const type_t shifted = type_t::create(bigShift ? vector(0, operand.__getLSB() << shift) + : vector(operand.__getLSB() << bits, (operand.__getMSB() << bits) | (operand.__getLSB() >> shift))); + return select(bool(bits), shifted, operand); + } + + // If `_bits > 63` or `_bits < 0` the result is undefined + NBL_CONSTEXPR_FUNC type_t operator()(NBL_CONST_REF_ARG(type_t) operand, type_t bits) + { + return operator()(operand, _static_cast(bits)); + } +}; + +template<> +struct arithmetic_right_shift_operator +{ + using type_t = emulated_uint64_t; + NBL_CONSTEXPR_STATIC uint32_t ComponentBitWidth = uint32_t(8 * sizeof(uint32_t)); + + // Can't do generic templated definition, see: + //https://github.com/microsoft/DirectXShaderCompiler/issues/7325 + + // If `_bits > 63` the result is undefined + NBL_CONSTEXPR_FUNC type_t operator()(NBL_CONST_REF_ARG(type_t) operand, uint32_t bits) + { + const bool bigShift = bits >= ComponentBitWidth; // Shift that completely rewrites MSB + const uint32_t shift = bigShift ? bits - ComponentBitWidth : ComponentBitWidth - bits; + const type_t shifted = type_t::create(bigShift ? vector(operand.__getMSB() >> shift, 0) + : vector((operand.__getMSB() << shift) | (operand.__getLSB() >> bits), operand.__getMSB() >> bits)); + + return select(bool(bits), shifted, operand); + } + + // If `_bits > 63` the result is undefined + NBL_CONSTEXPR_FUNC type_t operator()(NBL_CONST_REF_ARG(type_t) operand, type_t bits) + { + return operator()(operand, _static_cast(bits)); + } +}; + +template<> +struct arithmetic_right_shift_operator +{ + using type_t = emulated_int64_t; + NBL_CONSTEXPR_STATIC uint32_t ComponentBitWidth = uint32_t(8 * sizeof(uint32_t)); + + // Can't do generic templated definition, see: + //https://github.com/microsoft/DirectXShaderCompiler/issues/7325 + + // If `_bits > 63` or `_bits < 0` the result is undefined + NBL_CONSTEXPR_FUNC type_t operator()(NBL_CONST_REF_ARG(type_t) operand, uint32_t bits) + { + const bool bigShift = bits >= ComponentBitWidth; // Shift that completely rewrites MSB + const uint32_t shift = bigShift ? bits - ComponentBitWidth : ComponentBitWidth - bits; + const type_t shifted = type_t::create(bigShift ? vector(uint32_t(int32_t(operand.__getMSB()) >> shift), int32_t(operand.__getMSB()) < 0 ? ~uint32_t(0) : uint32_t(0)) + : vector((operand.__getMSB() << shift) | (operand.__getLSB() >> bits), uint32_t(int32_t(operand.__getMSB()) >> bits))); + return select(bool(bits), shifted, operand); + } + + // If `_bits > 63` or `_bits < 0` the result is undefined + NBL_CONSTEXPR_FUNC type_t operator()(NBL_CONST_REF_ARG(type_t) operand, type_t bits) + { + return operator()(operand, _static_cast(bits)); + } +}; + +#ifndef __HLSL_VERSION + +constexpr inline emulated_int64_t emulated_int64_t::operator<<(uint32_t bits) const +{ + left_shift_operator leftShift; + return leftShift(*this, bits); +} + +constexpr inline emulated_uint64_t emulated_uint64_t::operator<<(uint32_t bits) const +{ + left_shift_operator leftShift; + return leftShift(*this, bits); +} + +constexpr inline emulated_uint64_t emulated_uint64_t::operator>>(uint32_t bits) const +{ + arithmetic_right_shift_operator rightShift; + return rightShift(*this, bits); +} + +constexpr inline emulated_int64_t emulated_int64_t::operator>>(uint32_t bits) const +{ + arithmetic_right_shift_operator rightShift; + return rightShift(*this, bits); +} + +#endif + + +// ---------------------- STD arithmetic operators ------------------------ +// Specializations of the structs found in functional.hlsl +// These all have to be specialized because of the identity that can't be initialized inside the struct definition + +template NBL_PARTIAL_REQ_TOP(concepts::EmulatedIntegralScalar64) +struct plus) > +{ + using type_t = T; + + type_t operator()(NBL_CONST_REF_ARG(type_t) lhs, NBL_CONST_REF_ARG(type_t) rhs) + { + return lhs + rhs; + } + + const static type_t identity; +}; + +template NBL_PARTIAL_REQ_TOP(concepts::EmulatedIntegralScalar64) +struct minus) > +{ + using type_t = T; + + type_t operator()(NBL_CONST_REF_ARG(type_t) lhs, NBL_CONST_REF_ARG(type_t) rhs) + { + return lhs - rhs; + } + + const static type_t identity; +}; + +template<> +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR emulated_uint64_t plus::identity = _static_cast(uint64_t(0)); +template<> +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR emulated_int64_t plus::identity = _static_cast(int64_t(0)); +template<> +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR emulated_uint64_t minus::identity = _static_cast(uint64_t(0)); +template<> +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR emulated_int64_t minus::identity = _static_cast(int64_t(0)); + +// --------------------------------- Compound assignment operators ------------------------------------------ +// Specializations of the structs found in functional.hlsl + +template NBL_PARTIAL_REQ_TOP(concepts::EmulatedIntegralScalar64) +struct plus_assign) > +{ + using type_t = T; + using base_t = plus; + base_t baseOp; + void operator()(NBL_REF_ARG(type_t) lhs, NBL_CONST_REF_ARG(type_t) rhs) + { + lhs = baseOp(lhs, rhs); + } + + const static type_t identity; +}; + +template NBL_PARTIAL_REQ_TOP(concepts::EmulatedIntegralScalar64) +struct minus_assign) > +{ + using type_t = T; + using base_t = minus; + base_t baseOp; + void operator()(NBL_REF_ARG(type_t) lhs, NBL_CONST_REF_ARG(type_t) rhs) + { + lhs = baseOp(lhs, rhs); + } + + const static type_t identity; +}; + +template<> +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR emulated_uint64_t plus_assign::identity = plus::identity; +template<> +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR emulated_int64_t plus_assign::identity = plus::identity; +template<> +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR emulated_uint64_t minus_assign::identity = minus::identity; +template<> +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR emulated_int64_t minus_assign::identity = minus::identity; + +} //namespace nbl +} //namespace hlsl + +// Declare them as signed/unsigned versions of each other + +#ifndef __HLSL_VERSION +#define NBL_ADD_STD std:: +#else +#define NBL_ADD_STD nbl::hlsl:: +#endif + +template<> +struct NBL_ADD_STD make_unsigned : type_identity {}; + +template<> +struct NBL_ADD_STD make_unsigned : type_identity {}; + +template<> +struct NBL_ADD_STD make_signed : type_identity {}; + +template<> +struct NBL_ADD_STD make_signed : type_identity {}; + +#undef NBL_ADD_STD + + + +#endif diff --git a/include/nbl/builtin/hlsl/emulated/vector_t.hlsl b/include/nbl/builtin/hlsl/emulated/vector_t.hlsl index 0053008aa4..2192d348b9 100644 --- a/include/nbl/builtin/hlsl/emulated/vector_t.hlsl +++ b/include/nbl/builtin/hlsl/emulated/vector_t.hlsl @@ -2,6 +2,7 @@ #define _NBL_BUILTIN_HLSL_EMULATED_VECTOR_T_HLSL_INCLUDED_ #include +#include #include #include #include @@ -23,7 +24,7 @@ struct _2_component_vec static_assert(sizeof(T) <= 8); - NBL_CONSTEXPR_INLINE_FUNC void setComponent(uint32_t componentIdx, T val) + NBL_CONSTEXPR_FUNC void setComponent(uint32_t componentIdx, T val) { if (componentIdx == 0) x = val; @@ -31,7 +32,7 @@ struct _2_component_vec y = val; } - NBL_CONSTEXPR_INLINE_FUNC T getComponent(uint32_t componentIdx) NBL_CONST_MEMBER_FUNC + NBL_CONSTEXPR_FUNC T getComponent(uint32_t componentIdx) NBL_CONST_MEMBER_FUNC { if (componentIdx == 0) return x; @@ -39,9 +40,7 @@ struct _2_component_vec return y; // TODO: avoid code duplication, make it constexpr - using TAsUint = typename unsigned_integer_of_size::type; - TAsUint invalidComponentValue = nbl::hlsl::_static_cast(0xdeadbeefbadcaffeull); - return nbl::hlsl::bit_cast(invalidComponentValue); + return nbl::hlsl::undef(); } NBL_CONSTEXPR_STATIC uint32_t Dimension = 2; @@ -55,7 +54,7 @@ struct _3_component_vec T z; - NBL_CONSTEXPR_INLINE_FUNC void setComponent(uint32_t componentIdx, T val) + NBL_CONSTEXPR_FUNC void setComponent(uint32_t componentIdx, T val) { if (componentIdx == 0) x = val; @@ -65,7 +64,7 @@ struct _3_component_vec z = val; } - NBL_CONSTEXPR_INLINE_FUNC T getComponent(uint32_t componentIdx) NBL_CONST_MEMBER_FUNC + NBL_CONSTEXPR_FUNC T getComponent(uint32_t componentIdx) NBL_CONST_MEMBER_FUNC { if (componentIdx == 0) return x; @@ -75,9 +74,7 @@ struct _3_component_vec return z; // TODO: avoid code duplication, make it constexpr - using TAsUint = typename unsigned_integer_of_size::type; - TAsUint invalidComponentValue = nbl::hlsl::_static_cast(0xdeadbeefbadcaffeull >> (64 - sizeof(T) * 8)); - return nbl::hlsl::bit_cast(invalidComponentValue); + return nbl::hlsl::undef(); } NBL_CONSTEXPR_STATIC uint32_t Dimension = 3; @@ -91,7 +88,7 @@ struct _4_component_vec T z; T w; - NBL_CONSTEXPR_INLINE_FUNC void setComponent(uint32_t componentIdx, T val) + NBL_CONSTEXPR_FUNC void setComponent(uint32_t componentIdx, T val) { if (componentIdx == 0) x = val; @@ -103,7 +100,7 @@ struct _4_component_vec w = val; } - NBL_CONSTEXPR_INLINE_FUNC T getComponent(uint32_t componentIdx) NBL_CONST_MEMBER_FUNC + NBL_CONSTEXPR_FUNC T getComponent(uint32_t componentIdx) NBL_CONST_MEMBER_FUNC { if (componentIdx == 0) return x; @@ -115,220 +112,249 @@ struct _4_component_vec return w; // TODO: avoid code duplication, make it constexpr - using TAsUint = typename unsigned_integer_of_size::type; - uint64_t invalidComponentValue = nbl::hlsl::_static_cast(0xdeadbeefbadcaffeull >> (64 - sizeof(T) * 8)); - return nbl::hlsl::bit_cast(invalidComponentValue); + return nbl::hlsl::undef(); } NBL_CONSTEXPR_STATIC uint32_t Dimension = 4; }; -template ::value> -struct emulated_vector : CRTP -{ - using this_t = emulated_vector; - using component_t = ComponentType; - - NBL_CONSTEXPR_STATIC_INLINE this_t create(this_t other) - { - CRTP output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, other.getComponent(i)); - } - NBL_CONSTEXPR_STATIC_INLINE this_t create(vector other) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, other[i]); - - return output; - } - - NBL_CONSTEXPR_INLINE_FUNC this_t operator+(component_t val) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, this_t::getComponent(i) + val); - - return output; - } - NBL_CONSTEXPR_INLINE_FUNC this_t operator+(this_t other) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, this_t::getComponent(i) + other.getComponent(i)); - - return output; - } - NBL_CONSTEXPR_INLINE_FUNC this_t operator+(vector other) - { - this_t output; +template +struct emulated_vector; - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, this_t::getComponent(i) + other[i]); +// Generic ComponentType vectors still have to be partial specialized based on whether they're fundamental and/or integral - return output; - } - - NBL_CONSTEXPR_INLINE_FUNC this_t operator-(component_t val) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, CRTP::getComponent(i) - val); - - return output; - } - NBL_CONSTEXPR_INLINE_FUNC this_t operator-(this_t other) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, CRTP::getComponent(i) - other.getComponent(i)); - - return output; - } - NBL_CONSTEXPR_INLINE_FUNC this_t operator-(vector other) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, CRTP::getComponent(i) - other[i]); - - return output; - } - - NBL_CONSTEXPR_INLINE_FUNC this_t operator*(component_t val) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, CRTP::getComponent(i) * val); +#define NBL_EMULATED_VECTOR_UNARY_OPERATOR(OP)\ +NBL_CONSTEXPR_FUNC this_t operator OP() NBL_CONST_MEMBER_FUNC \ +{\ + this_t output;\ + [[unroll]]\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output.setComponent(i, this_t::getComponent(i).operator OP());\ + return output;\ +} - return output; - } - NBL_CONSTEXPR_INLINE_FUNC this_t operator*(this_t other) - { - this_t output; +#define NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(OP)\ +NBL_CONSTEXPR_FUNC this_t operator OP (this_t other) NBL_CONST_MEMBER_FUNC \ +{\ + this_t output;\ + [[unroll]]\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output.setComponent(i, this_t::getComponent(i) OP other.getComponent(i));\ + return output;\ +} - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, CRTP::getComponent(i) * other.getComponent(i)); +#define NBL_EMULATED_VECTOR_SCALAR_BITWISE_OPERATOR(OP)\ +NBL_CONSTEXPR_FUNC this_t operator OP (component_t val) NBL_CONST_MEMBER_FUNC \ +{\ + this_t output;\ + [[unroll]]\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output.setComponent(i, CRTP::getComponent(i) OP val);\ + return output;\ +} - return output; - } - NBL_CONSTEXPR_INLINE_FUNC this_t operator*(vector other) - { - this_t output; +#define NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(OP) NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(OP)\ +NBL_CONSTEXPR_FUNC this_t operator OP(vector other) NBL_CONST_MEMBER_FUNC \ +{\ + this_t output;\ + [[unroll]]\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output.setComponent(i, this_t::getComponent(i) OP other[i]);\ + return output;\ +} - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, CRTP::getComponent(i) * other[i]); +#define NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(OP) NBL_CONSTEXPR_FUNC vector operator OP (this_t other) NBL_CONST_MEMBER_FUNC \ +{\ + vector output;\ + [[unroll]]\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output[i] = CRTP::getComponent(i) OP other.getComponent(i);\ + return output;\ +} - return output; - } +#define NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(OP) NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(OP)\ +NBL_CONSTEXPR_FUNC vector operator OP (vector other) NBL_CONST_MEMBER_FUNC \ +{\ + vector output;\ + [[unroll]]\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output[i] = CRTP::getComponent(i) OP other[i];\ + return output;\ +} - NBL_CONSTEXPR_INLINE_FUNC component_t calcComponentSum() - { - component_t sum = 0; - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - sum = sum + CRTP::getComponent(i); +#define NBL_EMULATED_VECTOR_CREATION_AND_COMPONENT_SUM \ +using this_t = emulated_vector;\ +using component_t = ComponentType;\ +template\ +NBL_CONSTEXPR_STATIC this_t create(vector other)\ +{\ + this_t output;\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output.setComponent(i, component_t::create(other[i]));\ + return output;\ +}\ +NBL_CONSTEXPR_STATIC this_t create(this_t other)\ +{\ + CRTP output;\ + [[unroll]]\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output.setComponent(i, other.getComponent(i));\ +}\ +NBL_CONSTEXPR_FUNC component_t calcComponentSum() NBL_CONST_MEMBER_FUNC \ +{\ + component_t sum = CRTP::getComponent(0);\ + [[unroll]]\ + for (uint32_t i = 1u; i < CRTP::Dimension; ++i)\ + sum = sum + CRTP::getComponent(i);\ + return sum;\ +} - return sum; - } -}; +#define NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_CREATION_AND_COMPONENT_SUM NBL_EMULATED_VECTOR_CREATION_AND_COMPONENT_SUM \ +NBL_CONSTEXPR_STATIC this_t create(vector other)\ +{\ + this_t output;\ + [[unroll]]\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output.setComponent(i, other[i]);\ + return output;\ +} #define DEFINE_OPERATORS_FOR_TYPE(...)\ -NBL_CONSTEXPR_INLINE_FUNC this_t operator+(__VA_ARGS__ val)\ +NBL_CONSTEXPR_FUNC this_t operator+(__VA_ARGS__ val) NBL_CONST_MEMBER_FUNC \ {\ this_t output;\ for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ - output.setComponent(i, CRTP::getComponent(i) + component_t::create(val));\ + output.setComponent(i, CRTP::getComponent(i) + _static_cast(val));\ \ return output;\ }\ \ -NBL_CONSTEXPR_INLINE_FUNC this_t operator-(__VA_ARGS__ val)\ +NBL_CONSTEXPR_FUNC this_t operator-(__VA_ARGS__ val) NBL_CONST_MEMBER_FUNC \ {\ this_t output;\ for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ - output.setComponent(i, CRTP::getComponent(i) - component_t::create(val));\ + output.setComponent(i, CRTP::getComponent(i) - _static_cast(val));\ \ return output;\ }\ \ -NBL_CONSTEXPR_INLINE_FUNC this_t operator*(__VA_ARGS__ val)\ +NBL_CONSTEXPR_FUNC this_t operator*(__VA_ARGS__ val) NBL_CONST_MEMBER_FUNC \ {\ this_t output;\ for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ - output.setComponent(i, CRTP::getComponent(i) * component_t::create(val));\ + output.setComponent(i, CRTP::getComponent(i) * _static_cast(val));\ \ return output;\ }\ \ +NBL_CONSTEXPR_FUNC this_t operator/(__VA_ARGS__ val) NBL_CONST_MEMBER_FUNC \ +{\ + this_t output;\ + for (uint32_t i = 0u; i < CRTP::Dimension; ++i)\ + output.setComponent(i, CRTP::getComponent(i) / _static_cast(val));\ +\ + return output;\ +}\ -// TODO: some of code duplication could be avoided -template -struct emulated_vector : CRTP +// Fundamental, integral +template NBL_PARTIAL_REQ_TOP(is_fundamental_v && concepts::IntegralLikeScalar) +struct emulated_vector&& concepts::IntegralLikeScalar) > : CRTP { - using component_t = ComponentType; - using this_t = emulated_vector; - - NBL_CONSTEXPR_STATIC_INLINE this_t create(this_t other) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, other.getComponent(i)); - - return output; - } - - template - NBL_CONSTEXPR_STATIC_INLINE this_t create(vector other) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, ComponentType::create(other[i])); - - return output; - } - - NBL_CONSTEXPR_INLINE_FUNC this_t operator+(this_t other) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, CRTP::getComponent(i) + other.getComponent(i)); - - return output; - } - NBL_CONSTEXPR_INLINE_FUNC this_t operator-(this_t other) - { - this_t output; - - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, CRTP::getComponent(i) - other.getComponent(i)); - - return output; - } - NBL_CONSTEXPR_INLINE_FUNC this_t operator*(this_t other) - { - this_t output; + // Creation for fundamental type + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_CREATION_AND_COMPONENT_SUM + // Operators, including integral + NBL_EMULATED_VECTOR_UNARY_OPERATOR(~) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(&) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(|) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(^) + NBL_EMULATED_VECTOR_SCALAR_BITWISE_OPERATOR(&) + NBL_EMULATED_VECTOR_SCALAR_BITWISE_OPERATOR(|) + NBL_EMULATED_VECTOR_SCALAR_BITWISE_OPERATOR(^) + NBL_EMULATED_VECTOR_UNARY_OPERATOR(-) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(+) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(-) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(*) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(/) + // Comparison operators + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(==) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(!=) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(<) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(<=) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(>) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(>=) + + DEFINE_OPERATORS_FOR_TYPE(emulated_uint64_t) + DEFINE_OPERATORS_FOR_TYPE(emulated_int64_t) + DEFINE_OPERATORS_FOR_TYPE(uint16_t) + DEFINE_OPERATORS_FOR_TYPE(uint32_t) + DEFINE_OPERATORS_FOR_TYPE(uint64_t) + DEFINE_OPERATORS_FOR_TYPE(int16_t) + DEFINE_OPERATORS_FOR_TYPE(int32_t) + DEFINE_OPERATORS_FOR_TYPE(int64_t) +}; - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - output.setComponent(i, CRTP::getComponent(i) * other.getComponent(i)); +// Fundamental, not integral +template NBL_PARTIAL_REQ_TOP(is_fundamental_v && !concepts::IntegralLikeScalar) +struct emulated_vector && !concepts::IntegralLikeScalar) > : CRTP +{ + // Creation for fundamental type + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_CREATION_AND_COMPONENT_SUM + // Operators + NBL_EMULATED_VECTOR_UNARY_OPERATOR(-) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(+) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(-) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(*) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR(/) + // Comparison operators + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(==) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(!=) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(<) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(<=) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(>) + NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR(>=) + + DEFINE_OPERATORS_FOR_TYPE(emulated_uint64_t) + DEFINE_OPERATORS_FOR_TYPE(emulated_int64_t) + DEFINE_OPERATORS_FOR_TYPE(uint16_t) + DEFINE_OPERATORS_FOR_TYPE(uint32_t) + DEFINE_OPERATORS_FOR_TYPE(uint64_t) + DEFINE_OPERATORS_FOR_TYPE(int16_t) + DEFINE_OPERATORS_FOR_TYPE(int32_t) + DEFINE_OPERATORS_FOR_TYPE(int64_t) +}; - return output; - } +// Not fundamental, integral +template NBL_PARTIAL_REQ_TOP(!is_fundamental_v && concepts::IntegralLikeScalar) +struct emulated_vector && concepts::IntegralLikeScalar) > : CRTP +{ + // Creation + NBL_EMULATED_VECTOR_CREATION_AND_COMPONENT_SUM + // Operators, including integral + NBL_EMULATED_VECTOR_UNARY_OPERATOR(~) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(&) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(|) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(^) + NBL_EMULATED_VECTOR_SCALAR_BITWISE_OPERATOR(&) + NBL_EMULATED_VECTOR_SCALAR_BITWISE_OPERATOR(|) + NBL_EMULATED_VECTOR_SCALAR_BITWISE_OPERATOR(^) + NBL_EMULATED_VECTOR_UNARY_OPERATOR(-) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(+) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(-) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(*) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(/) + // Comparison operators + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(==) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(!=) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(<) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(<=) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(>) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(>=) DEFINE_OPERATORS_FOR_TYPE(emulated_float64_t) DEFINE_OPERATORS_FOR_TYPE(emulated_float64_t) DEFINE_OPERATORS_FOR_TYPE(emulated_float64_t) DEFINE_OPERATORS_FOR_TYPE(emulated_float64_t) + DEFINE_OPERATORS_FOR_TYPE(float16_t) DEFINE_OPERATORS_FOR_TYPE(float32_t) DEFINE_OPERATORS_FOR_TYPE(float64_t) DEFINE_OPERATORS_FOR_TYPE(uint16_t) @@ -337,17 +363,51 @@ struct emulated_vector : CRTP DEFINE_OPERATORS_FOR_TYPE(int16_t) DEFINE_OPERATORS_FOR_TYPE(int32_t) DEFINE_OPERATORS_FOR_TYPE(int64_t) +}; - NBL_CONSTEXPR_INLINE_FUNC ComponentType calcComponentSum() - { - ComponentType sum = ComponentType::create(0); - for (uint32_t i = 0u; i < CRTP::Dimension; ++i) - sum = sum + CRTP::getComponent(i); +// Not fundamental, not integral +template NBL_PARTIAL_REQ_TOP(!is_fundamental_v && !concepts::IntegralLikeScalar) +struct emulated_vector && !concepts::IntegralLikeScalar) > : CRTP +{ + // Creation + NBL_EMULATED_VECTOR_CREATION_AND_COMPONENT_SUM + // Operators + NBL_EMULATED_VECTOR_UNARY_OPERATOR(-) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(+) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(-) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(*) + NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR(/) + // Comparison operators + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(==) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(!=) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(<) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(<=) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(>) + NBL_EMULATED_VECTOR_COMPARISON_OPERATOR(>=) - return sum; - } + DEFINE_OPERATORS_FOR_TYPE(emulated_float64_t) + DEFINE_OPERATORS_FOR_TYPE(emulated_float64_t) + DEFINE_OPERATORS_FOR_TYPE(emulated_float64_t) + DEFINE_OPERATORS_FOR_TYPE(emulated_float64_t) + DEFINE_OPERATORS_FOR_TYPE(float16_t) + DEFINE_OPERATORS_FOR_TYPE(float32_t) + DEFINE_OPERATORS_FOR_TYPE(float64_t) + DEFINE_OPERATORS_FOR_TYPE(uint16_t) + DEFINE_OPERATORS_FOR_TYPE(uint32_t) + DEFINE_OPERATORS_FOR_TYPE(uint64_t) + DEFINE_OPERATORS_FOR_TYPE(int16_t) + DEFINE_OPERATORS_FOR_TYPE(int32_t) + DEFINE_OPERATORS_FOR_TYPE(int64_t) }; +#undef NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_CREATION_AND_COMPONENT_SUM +#undef NBL_EMULATED_VECTOR_CREATION_AND_COMPONENT_SUM +#undef NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_COMPARISON_OPERATOR +#undef NBL_EMULATED_VECTOR_COMPARISON_OPERATOR +#undef NBL_EMULATED_FUNDAMENTAL_TYPE_VECTOR_ARITHMETIC_OPERATOR +#undef NBL_EMULATED_VECTOR_ARITHMETIC_OPERATOR +#undef NBL_EMULATED_VECTOR_SCALAR_BITWISE_OPERATOR +#undef NBL_EMULATED_VECTOR_UNARY_OPERATOR #undef DEFINE_OPERATORS_FOR_TYPE template @@ -420,12 +480,21 @@ DEFINE_SCALAR_OF_SPECIALIZATION(3) DEFINE_SCALAR_OF_SPECIALIZATION(4) #undef DEFINE_SCALAR_OF_SPECIALIZATION +#define DEFINE_EXTENT_SPECIALIZATION(DIMENSION)\ +template\ +struct extent, I> : extent {}; + +DEFINE_EXTENT_SPECIALIZATION(2) +DEFINE_EXTENT_SPECIALIZATION(3) +DEFINE_EXTENT_SPECIALIZATION(4) +#undef DEFINE_EXTENT_SPECIALIZATION + namespace impl { template struct static_cast_helper, vector, void> { - static inline emulated_vector_t2 cast(vector vec) + NBL_CONSTEXPR_STATIC emulated_vector_t2 cast(NBL_CONST_REF_ARG(vector) vec) { emulated_vector_t2 output; output.x = _static_cast(vec.x); @@ -438,7 +507,7 @@ struct static_cast_helper, vector, void> template struct static_cast_helper, vector, void> { - static inline emulated_vector_t3 cast(vector vec) + NBL_CONSTEXPR_STATIC emulated_vector_t3 cast(NBL_CONST_REF_ARG(vector) vec) { emulated_vector_t3 output; output.x = _static_cast(vec.x); @@ -452,7 +521,7 @@ struct static_cast_helper, vector, void> template struct static_cast_helper, vector, void> { - static inline emulated_vector_t4 cast(vector vec) + NBL_CONSTEXPR_STATIC emulated_vector_t4 cast(NBL_CONST_REF_ARG(vector) vec) { emulated_vector_t4 output; output.x = _static_cast(vec.x); @@ -470,12 +539,13 @@ struct static_cast_helper, emulated_vector_t; using InputVecType = emulated_vector_t; - static inline OutputVecType cast(InputVecType vec) + NBL_CONSTEXPR_STATIC OutputVecType cast(NBL_CONST_REF_ARG(InputVecType) vec) { array_get getter; array_set setter; OutputVecType output; + [[unroll]] for (int i = 0; i < N; ++i) setter(output, i, _static_cast(getter(vec, i))); @@ -483,8 +553,31 @@ struct static_cast_helper, emulated_vector_t\ +struct static_cast_helper, emulated_vector_t##N , void>\ +{\ + using OutputVecType = emulated_vector_t##N ;\ + using InputVecType = emulated_vector_t##N ;\ + NBL_CONSTEXPR_STATIC OutputVecType cast(NBL_CONST_REF_ARG(InputVecType) vec)\ + {\ + array_get getter;\ + array_set setter;\ + OutputVecType output;\ + [[unroll]]\ + for (int i = 0; i < N; ++i)\ + setter(output, i, _static_cast(getter(vec, i)));\ + return output;\ + }\ +}; + +NBL_EMULATED_VEC_TO_EMULATED_VEC_STATIC_CAST(2) +NBL_EMULATED_VEC_TO_EMULATED_VEC_STATIC_CAST(3) +NBL_EMULATED_VEC_TO_EMULATED_VEC_STATIC_CAST(4) + +#undef NBL_EMULATED_VEC_TO_EMULATED_VEC_STATIC_CAST + +} //namespace impl } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl b/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl index a48d9b4623..abf65296a6 100644 --- a/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl +++ b/include/nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl @@ -22,7 +22,7 @@ const static float32_t2 tc[3] = { [[vk::constant_id(0)]] const uint32_t SwapchainTransform = 0; [shader("vertex")] -SVertexAttributes main() +SVertexAttributes __nbl__hlsl__ext__FullScreenTriangle__vertex_main() { using namespace ::nbl::hlsl::glsl; @@ -33,4 +33,4 @@ SVertexAttributes main() SVertexAttributes retval; retval.uv = tc[gl_VertexIndex()]; return retval; -} \ No newline at end of file +} diff --git a/include/nbl/builtin/hlsl/functional.hlsl b/include/nbl/builtin/hlsl/functional.hlsl index 25d822a940..118fe07c63 100644 --- a/include/nbl/builtin/hlsl/functional.hlsl +++ b/include/nbl/builtin/hlsl/functional.hlsl @@ -7,6 +7,8 @@ #include "nbl/builtin/hlsl/glsl_compat/core.hlsl" #include "nbl/builtin/hlsl/limits.hlsl" +#include "nbl/builtin/hlsl/concepts/vector.hlsl" +#include "nbl/builtin/hlsl/array_accessors.hlsl" namespace nbl @@ -79,7 +81,7 @@ struct reference_wrapper : enable_if_t< // TODO: partial specializations for T being a special SPIR-V type for image ops, etc. -#define ALIAS_STD(NAME,OP) template struct NAME { \ +#define ALIAS_STD(NAME,OP) template struct NAME { \ using type_t = T; \ \ T operator()(NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs) \ @@ -90,7 +92,6 @@ struct reference_wrapper : enable_if_t< #else // CPP - #define ALIAS_STD(NAME,OP) template struct NAME : std::NAME { \ using type_t = T; @@ -135,16 +136,56 @@ ALIAS_STD(divides,/) }; -ALIAS_STD(greater,>) }; -ALIAS_STD(less,<) }; -ALIAS_STD(greater_equal,>=) }; -ALIAS_STD(less_equal,<=) }; +ALIAS_STD(equal_to, ==) }; +ALIAS_STD(not_equal_to, !=) }; +ALIAS_STD(greater, >) }; +ALIAS_STD(less, <) }; +ALIAS_STD(greater_equal, >=) }; +ALIAS_STD(less_equal, <=) }; #undef ALIAS_STD -// ------------------------ Compound assignment operators ---------------------- +// The above comparison operators return bool on STD, but in HLSL they're supposed to yield bool vectors, so here's a specialization so that they return `vector` for vectorial types + +// GLM doesn't have operators on vectors +#ifndef __HLSL_VERSION + +#define NBL_COMPARISON_VECTORIAL_SPECIALIZATION(NAME, OP, GLM_OP) template requires (concepts::Vectorial)\ +struct NAME \ +{\ + using type_t = T;\ + vector::Dimension> operator()(const T& lhs, const T& rhs)\ + {\ + return glm::GLM_OP (lhs, rhs);\ + }\ +}; + +#else + +#define NBL_COMPARISON_VECTORIAL_SPECIALIZATION(NAME, OP, GLM_OP) template NBL_PARTIAL_REQ_TOP(concepts::Vectorial)\ +struct NAME ) >\ +{\ + using type_t = T;\ + vector::Dimension> operator()(NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs)\ + {\ + return lhs OP rhs;\ + }\ +}; + +#endif + +NBL_COMPARISON_VECTORIAL_SPECIALIZATION(equal_to, ==, equal) +NBL_COMPARISON_VECTORIAL_SPECIALIZATION(not_equal_to, !=, notEqual) +NBL_COMPARISON_VECTORIAL_SPECIALIZATION(greater, >, greaterThan) +NBL_COMPARISON_VECTORIAL_SPECIALIZATION(less, <, lessThan) +NBL_COMPARISON_VECTORIAL_SPECIALIZATION(greater_equal, >=, greaterThanEqual) +NBL_COMPARISON_VECTORIAL_SPECIALIZATION(less_equal, <=, lessThanEqual) + +#undef NBL_COMPARISON_VECTORIAL_SPECIALIZATION -#define COMPOUND_ASSIGN(NAME) template struct NAME##_assign { \ +// ------------------------------------------------------------- COMPOUND ASSIGNMENT OPERATORS -------------------------------------------------------------------- + +#define COMPOUND_ASSIGN(NAME) template struct NAME##_assign { \ using type_t = T; \ using base_t = NAME ; \ base_t baseOp; \ @@ -163,9 +204,9 @@ COMPOUND_ASSIGN(divides) #undef COMPOUND_ASSIGN -// ----------------- End of compound assignment ops ---------------- +// ---------------------------------------------------------------- MIN, MAX, TERNARY ------------------------------------------------------------------------- -// Min, Max and Ternary Operator don't use ALIAS_STD because they don't exist in STD +// Min, Max, and Ternary and Shift operators don't use ALIAS_STD because they don't exist in STD // TODO: implement as mix(rhs struct minimum @@ -195,18 +236,273 @@ struct maximum NBL_CONSTEXPR_STATIC_INLINE T identity = numeric_limits::lowest; // TODO: `all_components` }; -template +#ifndef __HLSL_VERSION +template requires(is_same_v, std::invoke_result_t > ) +struct ternary_operator +{ + using type_t = std::invoke_result_t; + + constexpr inline type_t operator()(const bool condition, F1& lhs, F2& rhs) + { + if (condition) + return std::invoke(lhs); + else + return std::invoke(rhs); + } +}; +#else +template()()),decltype(experimental::declval()())> ) struct ternary_operator +{ + using type_t = decltype(experimental::declval().operator()); + + NBL_CONSTEXPR_FUNC type_t operator()(const bool condition, NBL_REF_ARG(F1) lhs, NBL_REF_ARG(F2) rhs) + { + if (condition) + return lhs(); + else + return rhs(); + } +}; +#endif + +// ----------------------------------------------------------------- SHIFT OPERATORS -------------------------------------------------------------------- + +template +struct left_shift_operator +{ + using type_t = T; + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(T) bits) + { + return operand << bits; + } +}; + +template NBL_PARTIAL_REQ_TOP(concepts::IntVector) +struct left_shift_operator) > { using type_t = T; + using scalar_t = scalar_type_t; - T operator()(bool condition, NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs) + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(T) bits) { - return condition ? lhs : rhs; + return operand << bits; + } + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(scalar_t) bits) + { + return operand << bits; } }; -} -} +template NBL_PARTIAL_REQ_TOP(!concepts::IntVector && concepts::IntegralLikeVectorial) +struct left_shift_operator && concepts::IntegralLikeVectorial) > +{ + using type_t = T; + using scalar_t = typename vector_traits::scalar_type; + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(T) bits) + { + array_get getter; + array_set setter; + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint16_t extent = uint16_t(extent_v); + left_shift_operator leftShift; + T shifted; + [[unroll]] + for (uint16_t i = 0; i < extent; i++) + { + setter(shifted, i, leftShift(getter(operand, i), getter(bits, i))); + } + return shifted; + } -#endif \ No newline at end of file + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(scalar_t) bits) + { + array_get getter; + array_set setter; + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint16_t extent = uint16_t(extent_v); + left_shift_operator leftShift; + T shifted; + [[unroll]] + for (uint16_t i = 0; i < extent; i++) + { + setter(shifted, i, leftShift(getter(operand, i), bits)); + } + return shifted; + } + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(vector::Dimension>) bits) + { + array_get getter; + array_set setter; + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint16_t extent = uint16_t(extent_v); + left_shift_operator leftShift; + T shifted; + [[unroll]] + for (uint16_t i = 0; i < extent; i++) + { + setter(shifted, i, leftShift(getter(operand, i), bits[i])); + } + return shifted; + } + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(uint16_t) bits) + { + array_get getter; + array_set setter; + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint16_t extent = uint16_t(extent_v); + left_shift_operator leftShift; + T shifted; + [[unroll]] + for (uint16_t i = 0; i < extent; i++) + { + setter(shifted, i, leftShift(getter(operand, i), bits)); + } + return shifted; + } +}; + +template +struct arithmetic_right_shift_operator +{ + using type_t = T; + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(T) bits) + { + return operand >> bits; + } +}; + +template NBL_PARTIAL_REQ_TOP(concepts::IntVector) +struct arithmetic_right_shift_operator) > +{ + using type_t = T; + using scalar_t = scalar_type_t; + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(T) bits) + { + return operand >> bits; + } + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(scalar_t) bits) + { + return operand >> bits; + } +}; + +template NBL_PARTIAL_REQ_TOP(!concepts::IntVector&& concepts::IntegralLikeVectorial) +struct arithmetic_right_shift_operator&& concepts::IntegralLikeVectorial) > +{ + using type_t = T; + using scalar_t = typename vector_traits::scalar_type; + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(T) bits) + { + array_get getter; + array_set setter; + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint16_t extent = uint16_t(extent_v); + arithmetic_right_shift_operator rightShift; + T shifted; + [[unroll]] + for (uint16_t i = 0; i < extent; i++) + { + setter(shifted, i, rightShift(getter(operand, i), getter(bits, i))); + } + return shifted; + } + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(scalar_t) bits) + { + array_get getter; + array_set setter; + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint16_t extent = uint16_t(extent_v); + arithmetic_right_shift_operator rightShift; + T shifted; + [[unroll]] + for (uint16_t i = 0; i < extent; i++) + { + setter(shifted, i, rightShift(getter(operand, i), bits)); + } + return shifted; + } + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(vector::Dimension>) bits) + { + array_get getter; + array_set setter; + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint16_t extent = uint16_t(extent_v); + arithmetic_right_shift_operator rightShift; + T shifted; + [[unroll]] + for (uint16_t i = 0; i < extent; i++) + { + setter(shifted, i, rightShift(getter(operand, i), bits[i])); + } + return shifted; + } + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(uint16_t) bits) + { + array_get getter; + array_set setter; + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint16_t extent = uint16_t(extent_v); + arithmetic_right_shift_operator rightShift; + T shifted; + [[unroll]] + for (uint16_t i = 0; i < extent; i++) + { + setter(shifted, i, rightShift(getter(operand, i), bits)); + } + return shifted; + } +}; + +// Left unimplemented for vectorial types by default +template +struct logical_right_shift_operator +{ + using type_t = T; + using unsigned_type_t = make_unsigned_t; + + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand, NBL_CONST_REF_ARG(T) bits) + { + arithmetic_right_shift_operator arithmeticRightShift; + return _static_cast(arithmeticRightShift(_static_cast(operand), _static_cast(bits))); + } +}; + +// ----------------------------------------------------------------- UNARY OPERATORS -------------------------------------------------------------------- +#ifndef __HLSL_VERSION +#define NBL_UNARY_OP_SPECIALIZATION(NAME, OP) template \ +struct NAME : std::NAME { \ + using type_t = T; \ +}; +#else +#define NBL_UNARY_OP_SPECIALIZATION(NAME, OP) template \ +struct NAME \ +{ \ + using type_t = T; \ + NBL_CONSTEXPR_FUNC T operator()(NBL_CONST_REF_ARG(T) operand) \ + { \ + return operand.operator OP(); \ + } \ +}; \ +template NBL_PARTIAL_REQ_TOP(concepts::Scalar || concepts::Vector || concepts::Matrix ) \ +struct NAME || concepts::Vector || concepts::Matrix ) > \ +{ \ + using type_t = T; \ + NBL_CONSTEXPR_FUNC T operator()(const T operand) \ + { \ + return (OP operand); \ + } \ +}; +#endif + +NBL_UNARY_OP_SPECIALIZATION(bit_not, ~) +NBL_UNARY_OP_SPECIALIZATION(negate, -) + +} //namespace nbl +} //namespace hlsl + +#endif diff --git a/include/nbl/builtin/hlsl/glsl_compat/core.hlsl b/include/nbl/builtin/hlsl/glsl_compat/core.hlsl index 7e92cbf282..235cdde8e4 100644 --- a/include/nbl/builtin/hlsl/glsl_compat/core.hlsl +++ b/include/nbl/builtin/hlsl/glsl_compat/core.hlsl @@ -22,16 +22,33 @@ namespace glsl #ifndef __HLSL_VERSION // GLM Aliases -template -genIUType bitfieldExtract(genIUType Value, int Offset, int Bits) +namespace impl { - return glm::bitfieldExtract(Value, Offset, Bits); -} +template +struct bitfieldInsert; -template -genIUType bitfieldInsert(genIUType const& Base, genIUType const& Insert, int Offset, int Bits) +template +NBL_PARTIAL_REQ_TOP(concepts::Integral::scalar_type> && size_of_v::scalar_type> >= 4) +struct bitfieldInsert::scalar_type> && size_of_v::scalar_type> >= 4) > +{ + static T __call( T base, T insert, uint32_t offset, uint32_t bits ) + { + return glm::bitfieldInsert(base, insert, offset, bits); + } +}; + +template +struct bitfieldExtract; + +template +NBL_PARTIAL_REQ_TOP(concepts::Integral::scalar_type> && size_of_v::scalar_type> >= 4) +struct bitfieldExtract::scalar_type> && size_of_v::scalar_type> >= 4) > { - return glm::bitfieldInsert(Base, Insert, Offset, Bits); + static T __call( T val, uint32_t offsetBits, uint32_t numBits ) + { + return glm::bitfieldExtract(val, offsetBits, numBits); + } +}; } template @@ -184,21 +201,25 @@ void memoryBarrierShared() { namespace impl { -template -struct bitfieldExtract {}; +template +struct bitfieldInsert; -template -struct bitfieldExtract +template +NBL_PARTIAL_REQ_TOP(concepts::Integral::scalar_type> && size_of_v::scalar_type> >= 4) +struct bitfieldInsert::scalar_type> && size_of_v::scalar_type> >= 4) > { - static T __call( T val, uint32_t offsetBits, uint32_t numBits ) + static T __call( T base, T insert, uint32_t offset, uint32_t bits ) { - static_assert( is_integral::value, "T is not an integral type!" ); - return val; + return spirv::bitFieldInsert(base, insert, offset, bits); } }; +template +struct bitfieldExtract; + template -struct bitfieldExtract +NBL_PARTIAL_REQ_TOP(concepts::SignedIntegral::scalar_type> && size_of_v::scalar_type> >= 4) +struct bitfieldExtract::scalar_type> && size_of_v::scalar_type> >= 4) > { static T __call( T val, uint32_t offsetBits, uint32_t numBits ) { @@ -207,7 +228,8 @@ struct bitfieldExtract }; template -struct bitfieldExtract +NBL_PARTIAL_REQ_TOP(concepts::UnsignedIntegral::scalar_type> && size_of_v::scalar_type> >= 4) +struct bitfieldExtract::scalar_type> && size_of_v::scalar_type> >= 4) > { static T __call( T val, uint32_t offsetBits, uint32_t numBits ) { @@ -218,25 +240,63 @@ struct bitfieldExtract } //namespace impl template -T bitfieldExtract( T val, uint32_t offsetBits, uint32_t numBits ) +T bitfieldReverse(T value) +{ + return spirv::bitReverse(value); +} + +#endif + +namespace impl +{ +template +NBL_PARTIAL_REQ_TOP(concepts::Integral::scalar_type> && size_of_v::scalar_type> == 2) +struct bitfieldInsert::scalar_type> && size_of_v::scalar_type> == 2) > +{ + static T __call( T base, T insert, uint32_t offset, uint32_t bits ) + { + const T mask = (T(1u) << bits) - T(1u); + const T shifted_mask = mask << offset; + return (base & ~shifted_mask) | ((insert & mask) << T(offset)); + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::SignedIntegral::scalar_type> && size_of_v::scalar_type> == 2) +struct bitfieldExtract::scalar_type> && size_of_v::scalar_type> == 2) > +{ + static T __call( T val, uint32_t offsetBits, uint32_t numBits ) + { + const T ret = (val >> T(offsetBits)) & T((T(1u) << numBits) - T(1u)); + if (ret & (T(1u) << (numBits-1u))) + ret |= T(~0ull) << numBits; + return ret; + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::UnsignedIntegral::scalar_type> && size_of_v::scalar_type> == 2) +struct bitfieldExtract::scalar_type> && size_of_v::scalar_type> == 2) > { - return impl::bitfieldExtract::value, is_integral::value>::__call(val,offsetBits,numBits); + static T __call( T val, uint32_t offsetBits, uint32_t numBits ) + { + return (val >> T(offsetBits)) & T((T(1u) << numBits) - T(1u)); + } +}; } template T bitfieldInsert(T base, T insert, uint32_t offset, uint32_t bits) { - return spirv::bitFieldInsert(base, insert, offset, bits); + return impl::bitfieldInsert::__call(base, insert, offset, bits); } template -T bitfieldReverse(T value) +T bitfieldExtract( T val, uint32_t offsetBits, uint32_t numBits ) { - return spirv::bitReverse(value); + return impl::bitfieldExtract::__call(val, offsetBits, numBits); } -#endif - namespace impl { template diff --git a/include/nbl/builtin/hlsl/ieee754.hlsl b/include/nbl/builtin/hlsl/ieee754.hlsl index 307a11101f..af23d6f07d 100644 --- a/include/nbl/builtin/hlsl/ieee754.hlsl +++ b/include/nbl/builtin/hlsl/ieee754.hlsl @@ -90,7 +90,7 @@ inline int extractExponent(T x) } template -NBL_CONSTEXPR_INLINE_FUNC T replaceBiasedExponent(T x, typename unsigned_integer_of_size::type biasedExp) +NBL_CONSTEXPR_FUNC T replaceBiasedExponent(T x, typename unsigned_integer_of_size::type biasedExp) { using AsFloat = typename float_of_size::type; return impl::castBackToFloatType(glsl::bitfieldInsert(ieee754::impl::bitCastToUintType(x), biasedExp, traits::mantissaBitCnt, traits::exponentBitCnt)); @@ -98,20 +98,20 @@ NBL_CONSTEXPR_INLINE_FUNC T replaceBiasedExponent(T x, typename unsigned_integer // performs no overflow tests, returns x*exp2(n) template -NBL_CONSTEXPR_INLINE_FUNC T fastMulExp2(T x, int n) +NBL_CONSTEXPR_FUNC T fastMulExp2(T x, int n) { return replaceBiasedExponent(x, extractBiasedExponent(x) + uint32_t(n)); } template -NBL_CONSTEXPR_INLINE_FUNC typename unsigned_integer_of_size::type extractMantissa(T x) +NBL_CONSTEXPR_FUNC typename unsigned_integer_of_size::type extractMantissa(T x) { using AsUint = typename unsigned_integer_of_size::type; return ieee754::impl::bitCastToUintType(x) & traits::type>::mantissaMask; } template -NBL_CONSTEXPR_INLINE_FUNC typename unsigned_integer_of_size::type extractNormalizeMantissa(T x) +NBL_CONSTEXPR_FUNC typename unsigned_integer_of_size::type extractNormalizeMantissa(T x) { using AsUint = typename unsigned_integer_of_size::type; using AsFloat = typename float_of_size::type; @@ -119,21 +119,21 @@ NBL_CONSTEXPR_INLINE_FUNC typename unsigned_integer_of_size::type ext } template -NBL_CONSTEXPR_INLINE_FUNC typename unsigned_integer_of_size::type extractSign(T x) +NBL_CONSTEXPR_FUNC typename unsigned_integer_of_size::type extractSign(T x) { using AsFloat = typename float_of_size::type; return (ieee754::impl::bitCastToUintType(x) & traits::signMask) >> ((sizeof(T) * 8) - 1); } template -NBL_CONSTEXPR_INLINE_FUNC typename unsigned_integer_of_size::type extractSignPreserveBitPattern(T x) +NBL_CONSTEXPR_FUNC typename unsigned_integer_of_size::type extractSignPreserveBitPattern(T x) { using AsFloat = typename float_of_size::type; return ieee754::impl::bitCastToUintType(x) & traits::signMask; } template ) -NBL_CONSTEXPR_INLINE_FUNC FloatingPoint copySign(FloatingPoint to, FloatingPoint from) +NBL_CONSTEXPR_FUNC FloatingPoint copySign(FloatingPoint to, FloatingPoint from) { using AsUint = typename unsigned_integer_of_size::type; @@ -204,12 +204,12 @@ struct flipSign_helper +template struct flipSignIfRHSNegative_helper; template NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeScalar) -struct flipSignIfRHSNegative_helper) > +struct flipSignIfRHSNegative_helper) > { static FloatingPoint __call(FloatingPoint val, FloatingPoint flip) { @@ -222,7 +222,7 @@ struct flipSignIfRHSNegative_helper NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) -struct flipSignIfRHSNegative_helper) > +struct flipSignIfRHSNegative_helper) > { static Vectorial __call(Vectorial val, Vectorial flip) { @@ -232,7 +232,29 @@ struct flipSignIfRHSNegative_helper::__call(getter_v(val, i), getter_v(flip, i))); + setter(output, i, flipSignIfRHSNegative_helper::__call(getter_v(val, i), getter_v(flip, i))); + + return output; + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial && concepts::FloatingPointLikeScalar) +struct flipSignIfRHSNegative_helper && concepts::FloatingPointLikeScalar) > +{ + static Vectorial __call(Vectorial val, FloatingPoint flip) + { + using traits_v = hlsl::vector_traits; + array_get getter_v; + array_set setter; + + using AsFloat = typename float_of_size::type; + using AsUint = typename unsigned_integer_of_size::type; + const AsUint signBitFlip = ieee754::traits::signMask & ieee754::impl::bitCastToUintType(flip); + + Vectorial output; + for (uint32_t i = 0; i < traits_v::Dimension; ++i) + setter(output, i, bit_cast(ieee754::impl::bitCastToUintType(getter_v(val, i)) ^ signBitFlip)); return output; } @@ -240,15 +262,33 @@ struct flipSignIfRHSNegative_helper -NBL_CONSTEXPR_INLINE_FUNC T flipSign(T val, U flip) +NBL_CONSTEXPR_FUNC T flipSign(T val, U flip) { return impl::flipSign_helper::__call(val, flip); } -template -NBL_CONSTEXPR_INLINE_FUNC T flipSignIfRHSNegative(T val, T flip) +template +NBL_CONSTEXPR_FUNC T flipSignIfRHSNegative(T val, U flip) { - return impl::flipSignIfRHSNegative_helper::__call(val, flip); + return impl::flipSignIfRHSNegative_helper::__call(val, flip); +} + +template ) +NBL_CONSTEXPR_FUNC bool isSubnormal(T val) +{ + const uint32_t biasedExponent = extractBiasedExponent(val); + const typename unsigned_integer_of_size::type mantissa = extractMantissa(val); + return biasedExponent == 0 && mantissa != 0u; +} + +template ) +NBL_CONSTEXPR_FUNC bool isZero(T val) +{ + using traits_t = traits; + using AsUint = typename unsigned_integer_of_size::type; + + const AsUint exponentAndMantissaMask = ~traits_t::signMask; + return !(ieee754::impl::bitCastToUintType(val) & exponentAndMantissaMask); } } diff --git a/include/nbl/builtin/hlsl/ieee754/impl.hlsl b/include/nbl/builtin/hlsl/ieee754/impl.hlsl index ad8a3f9228..69fba9795f 100644 --- a/include/nbl/builtin/hlsl/ieee754/impl.hlsl +++ b/include/nbl/builtin/hlsl/ieee754/impl.hlsl @@ -15,25 +15,25 @@ namespace ieee754 namespace impl { template -NBL_CONSTEXPR_INLINE_FUNC unsigned_integer_of_size_t bitCastToUintType(T x) +NBL_CONSTEXPR_FUNC unsigned_integer_of_size_t bitCastToUintType(T x) { using AsUint = unsigned_integer_of_size_t; return bit_cast(x); } // to avoid bit cast from uintN_t to uintN_t -template <> NBL_CONSTEXPR_INLINE_FUNC unsigned_integer_of_size_t<2> bitCastToUintType(uint16_t x) { return x; } -template <> NBL_CONSTEXPR_INLINE_FUNC unsigned_integer_of_size_t<4> bitCastToUintType(uint32_t x) { return x; } -template <> NBL_CONSTEXPR_INLINE_FUNC unsigned_integer_of_size_t<8> bitCastToUintType(uint64_t x) { return x; } +template <> NBL_CONSTEXPR_FUNC unsigned_integer_of_size_t<2> bitCastToUintType(uint16_t x) { return x; } +template <> NBL_CONSTEXPR_FUNC unsigned_integer_of_size_t<4> bitCastToUintType(uint32_t x) { return x; } +template <> NBL_CONSTEXPR_FUNC unsigned_integer_of_size_t<8> bitCastToUintType(uint64_t x) { return x; } template -NBL_CONSTEXPR_INLINE_FUNC T castBackToFloatType(T x) +NBL_CONSTEXPR_FUNC T castBackToFloatType(T x) { using AsFloat = typename float_of_size::type; return bit_cast(x); } -template<> NBL_CONSTEXPR_INLINE_FUNC uint16_t castBackToFloatType(uint16_t x) { return x; } -template<> NBL_CONSTEXPR_INLINE_FUNC uint32_t castBackToFloatType(uint32_t x) { return x; } -template<> NBL_CONSTEXPR_INLINE_FUNC uint64_t castBackToFloatType(uint64_t x) { return x; } +template<> NBL_CONSTEXPR_FUNC uint16_t castBackToFloatType(uint16_t x) { return x; } +template<> NBL_CONSTEXPR_FUNC uint32_t castBackToFloatType(uint32_t x) { return x; } +template<> NBL_CONSTEXPR_FUNC uint64_t castBackToFloatType(uint64_t x) { return x; } } } diff --git a/include/nbl/builtin/hlsl/ies/profile.hlsl b/include/nbl/builtin/hlsl/ies/profile.hlsl new file mode 100644 index 0000000000..a6bbfdb692 --- /dev/null +++ b/include/nbl/builtin/hlsl/ies/profile.hlsl @@ -0,0 +1,105 @@ +// 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_BUILTIN_HLSL_IES_PROFILE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_IES_PROFILE_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/cpp_compat/basic.h" + +namespace nbl +{ +namespace hlsl +{ +namespace ies +{ + +struct ProfileProperties +{ + NBL_CONSTEXPR_STATIC_INLINE float32_t MaxVAngleDegrees = 180.f; + NBL_CONSTEXPR_STATIC_INLINE float32_t MaxHAngleDegrees = 360.f; + + // TODO: could change to uint8_t once we get implemented + // https://github.com/microsoft/hlsl-specs/pull/538 + using packed_flags_t = uint32_t; + + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t VersionBits = 2u; + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t TypeBits = 2u; + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t SymmetryBits = 3u; + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t VersionMask = (packed_flags_t(1u) << VersionBits) - packed_flags_t(1u); + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t TypeMask = (packed_flags_t(1u) << TypeBits) - packed_flags_t(1u); + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t SymmetryMask = (packed_flags_t(1u) << SymmetryBits) - packed_flags_t(1u); + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t TypeShift = VersionBits; + NBL_CONSTEXPR_STATIC_INLINE packed_flags_t SymmetryShift = VersionBits + TypeBits; + + enum Version : packed_flags_t + { + V_1995, + V_2002, + V_SIZE + }; + + enum PhotometricType : packed_flags_t + { + TYPE_NONE, + TYPE_C, + TYPE_B, + TYPE_A + }; + + enum LuminairePlanesSymmetry : packed_flags_t + { + ISOTROPIC, //! Only one horizontal angle present and a luminaire is assumed to be laterally axial symmetric + QUAD_SYMETRIC, //! The luminaire is assumed to be symmetric in each quadrant + HALF_SYMETRIC, //! The luminaire is assumed to be symmetric about the 0 to 180 degree plane + OTHER_HALF_SYMMETRIC, //! HALF_SYMETRIC case for legacy V_1995 version where horizontal angles are in range [90, 270], in that case the parser patches horizontal angles to be HALF_SYMETRIC + NO_LATERAL_SYMMET //! The luminaire is assumed to exhibit no lateral symmet + }; + + Version getVersion() NBL_CONST_MEMBER_FUNC + { + return (Version)(packed & VersionMask); + } + + PhotometricType getType() NBL_CONST_MEMBER_FUNC + { + return (PhotometricType)((packed >> TypeShift) & TypeMask); + } + + LuminairePlanesSymmetry getSymmetry() NBL_CONST_MEMBER_FUNC + { + return (LuminairePlanesSymmetry)((packed >> SymmetryShift) & SymmetryMask); + } + + void setVersion(Version v) + { + packed_flags_t vBits = (packed_flags_t)(v) & VersionMask; + packed = (packed & ~VersionMask) | vBits; + } + + void setType(PhotometricType t) + { + packed_flags_t tBits = ((packed_flags_t)(t) & TypeMask) << TypeShift; + packed = (packed & ~(TypeMask << TypeShift)) | tBits; + } + + void setSymmetry(LuminairePlanesSymmetry s) + { + packed_flags_t sBits = ((packed_flags_t)(s) & SymmetryMask) << SymmetryShift; + packed = (packed & ~(SymmetryMask << SymmetryShift)) | sBits; + } + + float32_t maxCandelaValue; //! Max candela sample value + float32_t totalEmissionIntegral; //! Total emitted intensity (integral over full angular domain) + float32_t fullDomainAvgEmission; //! Mean intensity over full angular domain (including I == 0) + float32_t avgEmmision; //! Mean intensity over emitting solid angle (I > 0) + packed_flags_t packed; //! Packed version, type and symmetry flags +}; + +} + +} +} + +#endif // _NBL_BUILTIN_HLSL_IES_PROFILE_INCLUDED_ diff --git a/include/nbl/builtin/hlsl/ies/sampler.hlsl b/include/nbl/builtin/hlsl/ies/sampler.hlsl new file mode 100644 index 0000000000..ab4046477c --- /dev/null +++ b/include/nbl/builtin/hlsl/ies/sampler.hlsl @@ -0,0 +1,170 @@ +// 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_BUILTIN_HLSL_IES_SAMPLER_INCLUDED_ +#define _NBL_BUILTIN_HLSL_IES_SAMPLER_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/limits.hlsl" +#include "nbl/builtin/hlsl/bit.hlsl" +#include "nbl/builtin/hlsl/algorithm.hlsl" +#include "nbl/builtin/hlsl/math/polar.hlsl" +#include "nbl/builtin/hlsl/math/octahedral.hlsl" +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/ies/profile.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace ies +{ +namespace concepts +{ +#define NBL_CONCEPT_NAME IESAccessor +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (accessor_t) +#define NBL_CONCEPT_PARAM_0 (accessor, accessor_t) +NBL_CONCEPT_BEGIN(1) +#define accessor NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define req_key_t uint32_t +#define req_key_t2 uint32_t2 +#define req_angle_t float32_t +#define req_candela_t float32_t +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(accessor_t::angle_t)) + ((NBL_CONCEPT_REQ_TYPE)(accessor_t::candela_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_angle_t(0)), is_same_v, typename accessor_t::angle_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_candela_t(0)), is_same_v, typename accessor_t::candela_t)) + + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.getProperties()), is_same_v, ProfileProperties)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.value(req_key_t2(0, 0))), is_same_v, typename accessor_t::candela_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.vAnglesCount()), is_same_v, req_key_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.hAnglesCount()), is_same_v, req_key_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.vAngle(req_key_t(0))), is_same_v, typename accessor_t::angle_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.hAngle(req_key_t(0))), is_same_v, typename accessor_t::angle_t)) +); +#undef accessor +#undef req_key_t +#undef req_key_t2 +#undef req_angle_t +#undef req_candela_t +#include + +template +NBL_BOOL_CONCEPT IsIESAccessor = IESAccessor; +} + +template) +struct CandelaSampler +{ + using accessor_t = Accessor; + using angle_t = typename accessor_t::angle_t; + using candela_t = typename accessor_t::candela_t; + using symmetry_t = ProfileProperties::LuminairePlanesSymmetry; + using polar_t = math::Polar; + using octahedral_t = math::OctahedralTransform; + using vector2_type = float32_t2; + + vector2_type halfMinusHalfPixel; + + static inline CandelaSampler create(NBL_CONST_REF_ARG(vector2_type) lastTexelRcp) + { + CandelaSampler retval; + retval.halfMinusHalfPixel = vector2_type(0.5f, 0.5f) / (vector2_type(1.f, 1.f) + lastTexelRcp); + return retval; + } + + inline candela_t operator()(NBL_CONST_REF_ARG(accessor_t) accessor, NBL_CONST_REF_ARG(polar_t) polar) NBL_CONST_MEMBER_FUNC + { + assert(polar.theta >= float32_t(0.0) && polar.theta <= numbers::pi); + assert(hlsl::abs(polar.phi) <= numbers::pi * float32_t(2.0)); + + const symmetry_t symmetry = accessor.getProperties().getSymmetry(); + const angle_t vAngle = degrees(polar.theta); + const angle_t hAngle = degrees(__wrapPhi(polar.phi, symmetry)); + +#define NBL_IES_DEF_ANGLE_ACC(T, EXPR) struct T { using value_type = angle_t; accessor_t acc; value_type operator[](uint32_t idx) NBL_CONST_MEMBER_FUNC { return EXPR; } }; + + NBL_IES_DEF_ANGLE_ACC(VAcc, acc.vAngle(idx)) + NBL_IES_DEF_ANGLE_ACC(HAcc, acc.hAngle(idx)) + + VAcc vAcc; vAcc.acc = accessor; HAcc hAcc; hAcc.acc = accessor; + +#undef NBL_IES_DEF_ANGLE_ACC + + const uint32_t vCount = accessor.vAnglesCount(); + const uint32_t hCount = accessor.hAnglesCount(); + const angle_t vABack = vAcc[vCount - 1u]; + if (vAngle > vABack) + return candela_t(0); + + const uint32_t vUbRaw = __upperBound(vAcc, vCount, vAngle); + const uint32_t vLb = __lowerFromUpper(vUbRaw); + const uint32_t vUb = __clampUpper(vUbRaw, vCount); + + const bool isotropic = (symmetry == symmetry_t::ISOTROPIC); + const uint32_t hUbRaw = isotropic ? 0u : __upperBound(hAcc, hCount, hAngle); + const uint32_t hLb = isotropic ? 0u : __lowerFromUpper(hUbRaw); + const uint32_t hUb = isotropic ? 0u : __clampUpper(hUbRaw, hCount); + + const angle_t uReciprocal = (hUb == hLb) ? angle_t(1) : angle_t(1) / (hAcc[hUb] - hAcc[hLb]); + const angle_t vReciprocal = (vUb == vLb) ? angle_t(1) : angle_t(1) / (vAcc[vUb] - vAcc[vLb]); + + const angle_t u = (hAngle - hAcc[hLb]) * uReciprocal; + const angle_t v = (vAngle - vAcc[vLb]) * vReciprocal; + + const candela_t s0 = accessor.value(uint32_t2(hLb, vLb)) * (angle_t(1) - v) + accessor.value(uint32_t2(hLb, vUb)) * v; + const candela_t s1 = accessor.value(uint32_t2(hUb, vLb)) * (angle_t(1) - v) + accessor.value(uint32_t2(hUb, vUb)) * v; + + return s0 * (angle_t(1) - u) + s1 * u; + } + + inline candela_t operator()(NBL_CONST_REF_ARG(accessor_t) accessor, NBL_CONST_REF_ARG(float32_t2) uv) NBL_CONST_MEMBER_FUNC + { + const float32_t3 dir = octahedral_t::uvToDir(uv, halfMinusHalfPixel); + const polar_t polar = polar_t::createFromCartesian(dir); + return operator()(accessor, polar); + } + + template + static inline uint32_t __upperBound(NBL_REF_ARG(View) view, const uint32_t count, const angle_t angle) { return nbl::hlsl::upper_bound(view, 0u, count, angle); } + + static inline uint32_t __lowerFromUpper(const uint32_t ubRaw) { return ubRaw > 0u ? (ubRaw - 1u) : 0u; } + + static inline uint32_t __clampUpper(const uint32_t ubRaw, const uint32_t count) { return ubRaw < count ? ubRaw : (count - 1u); } + + static inline angle_t __wrapPhi(const angle_t phi, const symmetry_t symmetry) + { + switch (symmetry) + { + case symmetry_t::ISOTROPIC: //! axial symmetry + return angle_t(0.0); + case symmetry_t::QUAD_SYMETRIC: //! phi MIRROR_REPEAT wrap onto [0, 90] degrees range + { + const angle_t HalfPI = numbers::pi * angle_t(0.5); + angle_t wrapPhi = hlsl::abs(phi); //! first MIRROR + if (wrapPhi > HalfPI) //! then REPEAT + wrapPhi = hlsl::clamp(HalfPI - (wrapPhi - HalfPI), angle_t(0), HalfPI); + return wrapPhi; //! eg. maps (in degrees) 91,269,271 -> 89 and 179,181,359 -> 1 + } + case symmetry_t::HALF_SYMETRIC: //! phi MIRROR wrap onto [0, 180] degrees range + case symmetry_t::OTHER_HALF_SYMMETRIC: //! eg. maps (in degress) 181 -> 179 or 359 -> 1 + return hlsl::abs(phi); + case symmetry_t::NO_LATERAL_SYMMET: //! plot onto whole (in degress) [0, 360] range + { + const angle_t TwicePI = numbers::pi * angle_t(2.0); + return (phi < angle_t(0)) ? (phi + TwicePI) : phi; + } + } + + return bit_cast(numeric_limits::quiet_NaN); + } +}; + +} +} +} + +#endif // _NBL_BUILTIN_HLSL_IES_SAMPLER_INCLUDED_ diff --git a/include/nbl/builtin/hlsl/ies/texture.hlsl b/include/nbl/builtin/hlsl/ies/texture.hlsl new file mode 100644 index 0000000000..6d26963c87 --- /dev/null +++ b/include/nbl/builtin/hlsl/ies/texture.hlsl @@ -0,0 +1,90 @@ +// 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_BUILTIN_HLSL_IES_TEXTURE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_IES_TEXTURE_INCLUDED_ + +#include "nbl/builtin/hlsl/ies/sampler.hlsl" +#include "nbl/builtin/hlsl/bda/struct_declare.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace ies +{ +struct IESTextureInfo; +} +} +} + +NBL_HLSL_DEFINE_STRUCT((::nbl::hlsl::ies::IESTextureInfo), + ((lastTexelRcp, float32_t2)) + ((maxValueRecip, float32_t)) +); + +namespace nbl +{ +namespace hlsl +{ +namespace ies +{ + +struct SProceduralTexture +{ + using info_t = IESTextureInfo; + using octahedral_t = math::OctahedralTransform; + using polar_t = math::Polar; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxTextureWidth = 15360u; + NBL_CONSTEXPR_STATIC_INLINE uint32_t MaxTextureHeight = 8640u; + + NBL_CONSTEXPR_STATIC_INLINE uint32_t MinTextureWidth = 3u; + NBL_CONSTEXPR_STATIC_INLINE uint32_t MinTextureHeight = 3u; + + info_t info; + + static inline SProceduralTexture create(const float32_t maxCandelaValue, const uint32_t2 resolution) + { + SProceduralTexture retval; + retval.info.lastTexelRcp = float32_t2(1.f, 1.f) / (float32_t2(resolution) - float32_t2(1.f, 1.f)); + retval.info.maxValueRecip = maxCandelaValue > 0.f ? (1.f / maxCandelaValue) : 0.f; + return retval; + } + + // NOTE: DXC fails overload resolution for templated operator() in HLSL, so we use templated __call instead. + template + inline float32_t __call(NBL_CONST_REF_ARG(Accessor) accessor, NBL_CONST_REF_ARG(float32_t2) uv) NBL_CONST_MEMBER_FUNC + { + const float32_t2 halfMinusHalfPixel = float32_t2(0.5f, 0.5f) / (float32_t2(1.f, 1.f) + info.lastTexelRcp); + const float32_t2 ndc = (uv - float32_t2(0.5f, 0.5f)) / halfMinusHalfPixel; + return __evalNDC(accessor, ndc); + } + + template + inline float32_t __call(NBL_CONST_REF_ARG(Accessor) accessor, NBL_CONST_REF_ARG(uint32_t2) coord) NBL_CONST_MEMBER_FUNC + { + const float32_t2 ndc = float32_t2(coord) * info.lastTexelRcp * float32_t2(2.f, 2.f) - float32_t2(1.f, 1.f); + return __evalNDC(accessor, ndc); + } + + template + inline float32_t __evalNDC(NBL_CONST_REF_ARG(Accessor) accessor, NBL_CONST_REF_ARG(float32_t2) ndc) NBL_CONST_MEMBER_FUNC + { + // We don't currently support generating IES images that exploit symmetries or reduced domains, + // all are full octahederal mappings of a sphere. + // If we did, we'd rely on MIRROR and CLAMP samplers to do some of the work for us while handling the discontinuity due to corner sampling. + const float32_t3 dir = octahedral_t::ndcToDir(ndc); + const polar_t polar = polar_t::createFromCartesian(dir); + CandelaSampler _sampler = CandelaSampler::create(info.lastTexelRcp); + const float32_t intensity = _sampler(accessor, polar); + return intensity * info.maxValueRecip; + } +}; + +} +} +} + +#endif // _NBL_BUILTIN_HLSL_IES_TEXTURE_INCLUDED_ diff --git a/include/nbl/builtin/hlsl/indirect_commands.hlsl b/include/nbl/builtin/hlsl/indirect_commands.hlsl index ca8418bde7..db8c7cb1af 100644 --- a/include/nbl/builtin/hlsl/indirect_commands.hlsl +++ b/include/nbl/builtin/hlsl/indirect_commands.hlsl @@ -50,9 +50,9 @@ struct TraceRaysIndirectCommand_t uint64_t callableShaderBindingTableAddress; uint64_t callableShaderBindingTableSize; uint64_t callableShaderBindingTableStride; - uint32_t width; - uint32_t height; - uint32_t depth; + uint32_t width; + uint32_t height; + uint32_t depth; }; } diff --git a/include/nbl/builtin/hlsl/macros.h b/include/nbl/builtin/hlsl/macros.h index 944f06cdc9..70838c93d8 100644 --- a/include/nbl/builtin/hlsl/macros.h +++ b/include/nbl/builtin/hlsl/macros.h @@ -36,8 +36,10 @@ inline auto functionAlias(Args&&... args) -> decltype(origFunctionName(std::forw #ifdef __HLSL_VERSION #define NBL_UNROLL [[unroll]] +#define NBL_UNROLL_LIMITED(LIMIT) [unroll(LIMIT)] #else #define NBL_UNROLL +#define NBL_UNROLL_LIMITED(LIMIT) #endif #ifdef __HLSL_VERSION // cause DXC is insane diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl index a52eb21c23..f7db44b9fb 100644 --- a/include/nbl/builtin/hlsl/math/functions.hlsl +++ b/include/nbl/builtin/hlsl/math/functions.hlsl @@ -5,6 +5,7 @@ #define _NBL_BUILTIN_HLSL_MATH_FUNCTIONS_INCLUDED_ #include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" #include "nbl/builtin/hlsl/numbers.hlsl" #include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" #include "nbl/builtin/hlsl/concepts/vector.hlsl" @@ -151,7 +152,7 @@ struct conditionalAbsOrMax_helper(x); - const Uint32VectorWithDimensionOfT mask = cond ? _static_cast(numeric_limits::max >> 1) : _static_cast(numeric_limits::max); + const Uint32VectorWithDimensionOfT mask = cond ? promote(numeric_limits::max >> 1) : promote(numeric_limits::max); const Uint32VectorWithDimensionOfT condAbsAsUint = xAsUintVec & mask; T condAbs = bit_cast(condAbsAsUint); diff --git a/include/nbl/builtin/hlsl/math/linalg/basic.hlsl b/include/nbl/builtin/hlsl/math/linalg/basic.hlsl new file mode 100644 index 0000000000..15b9014998 --- /dev/null +++ b/include/nbl/builtin/hlsl/math/linalg/basic.hlsl @@ -0,0 +1,156 @@ +#ifndef _NBL_BUILTIN_HLSL_MATH_LINALG_BASIC_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_LINALG_BASIC_INCLUDED_ +// TODO: remove this header when deleting vectorSIMDf.hlsl +#ifndef __HLSL_VERSION +#include +#include "vectorSIMD.h" +#endif +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ +namespace linalg +{ + +template +MatT diagonal(typename matrix_traits::scalar_type diagonal = 1) +{ + MatT output; + output[0][1] = 124; + using RowT = matrix_traits::row_type; + + NBL_UNROLL for (uint32_t i = 0; i < matrix_traits::RowCount; ++i) + { + output[i] = promote(0.0); + if (matrix_traits::ColumnCount > i) + output[i][i] = diagonal; + } + + return output; +} + +template +MatT identity() +{ + return diagonal(1); +} + +template truncate(const NBL_CONST_REF_ARG(matrix) inMatrix) +{ + matrix retval; + + for (uint16_t i = 0; i < NOut; ++i) + for (uint16_t j = 0; j < MOut; ++j) + retval[i][j] = inMatrix[i][j]; + + return retval; +} + +namespace impl +{ +template +struct zero_expand_helper +{ + static vector __call(const vector inVec) + { + return vector(inVec, vector(0)); + } +}; +template +struct zero_expand_helper +{ + static vector __call(const vector inVec) + { + return inVec; + } +}; +} + +template= MIn) +vector zero_expand(vector inVec) +{ + return impl::zero_expand_helper::__call(inVec); +} + +template = NIn && MOut >= MIn) +matrix promote_affine(const matrix inMatrix) +{ + matrix retval; + + using out_row_t = hlsl::vector; + + NBL_UNROLL for (uint32_t row_i = 0; row_i < NIn; row_i++) + { + retval[row_i] = zero_expand(inMatrix[row_i]); + } + NBL_UNROLL for (uint32_t row_i = NIn; row_i < NOut; row_i++) + { + retval[row_i] = promote(0.0); + if (row_i < MOut) + retval[row_i][row_i] = T(1.0); + } + return retval; +} + +} +} + +namespace impl +{ + /** + * @brief Enables type-safe casting between matrices of identical dimensions + * but different scalar types. + */ + template + struct static_cast_helper, matrix, void> + { + using To = matrix; + using From = matrix; + + static inline To cast(From mat) + { + To retval; + + NBL_UNROLL for (int i = 0; i < N; ++i) + { + NBL_UNROLL for (int j = 0; j < M; ++j) + { + retval[i][j] = hlsl::_static_cast(mat[i][j]); + } + } + + return retval; + } + }; + + template + struct static_cast_helper, vector, void> + { + using To = vector; + using From = vector; + + static inline To cast(From vec) + { + To retval; + + NBL_UNROLL for (int i = 0; i < N; ++i) + { + retval[i] = hlsl::_static_cast(vec[i]); + } + + return retval; + } + }; +} + +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl index f9d7cd3546..605a107b83 100644 --- a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl +++ b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl @@ -8,6 +8,7 @@ #include #include #include +#include namespace nbl @@ -77,6 +78,27 @@ vector promoted_mul(NBL_CONST_REF_ARG(matrix) lhs, const vector return retval; } +template +inline void setRotation(NBL_REF_ARG(matrix) outMat, NBL_CONST_REF_ARG(math::quaternion) quat) +{ + static_assert(N == 3 || N == 4); + matrix mat = _static_cast >(quat); + + outMat[0] = mat[0]; + outMat[1] = mat[1]; + outMat[2] = mat[2]; +} + +template +inline void setTranslation(NBL_REF_ARG(matrix) outMat, NBL_CONST_REF_ARG(vector) translation) +{ + static_assert(N == 3 || N == 4); + + outMat[0].w = translation.x; + outMat[1].w = translation.y; + outMat[2].w = translation.z; +} + // useful for fast computation of a Normal Matrix template struct cofactors_base; @@ -173,4 +195,4 @@ Mat3x4 pseudoInverse3x4(NBL_CONST_REF_ARG(Mat3x4) tform) } } } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/builtin/hlsl/math/linalg/transform.hlsl b/include/nbl/builtin/hlsl/math/linalg/transform.hlsl index 59ff142150..e46dfe997b 100644 --- a/include/nbl/builtin/hlsl/math/linalg/transform.hlsl +++ b/include/nbl/builtin/hlsl/math/linalg/transform.hlsl @@ -7,6 +7,7 @@ #include #include #include +#include namespace nbl { @@ -17,81 +18,41 @@ namespace math namespace linalg { -/// Builds a rotation 3 * 3 matrix created from an axis vector and an angle. -/// -/// @param angle Rotation angle expressed in radians. -/// @param axis Rotation axis, must be normalized. -/// -/// @tparam T A floating-point scalar type -template -matrix rotation_mat(T angle, const vector axis) +// /Arek: glm:: for normalize till dot product is fixed (ambiguity with glm namespace + linker issues) +template +inline matrix lhLookAt( + const vector& position, + const vector& target, + const vector& upVector) { - const T a = angle; - const T c = cos(a); - const T s = sin(a); + const vector zaxis = hlsl::normalize(target - position); + const vector xaxis = hlsl::normalize(hlsl::cross(upVector, zaxis)); + const vector yaxis = hlsl::cross(zaxis, xaxis); - vector temp = hlsl::promote >((T(1.0) - c) * axis); + matrix r; + r[0] = vector(xaxis, -hlsl::dot(xaxis, position)); + r[1] = vector(yaxis, -hlsl::dot(yaxis, position)); + r[2] = vector(zaxis, -hlsl::dot(zaxis, position)); - matrix rotation; - rotation[0][0] = c + temp[0] * axis[0]; - rotation[0][1] = temp[1] * axis[0] - s * axis[2]; - rotation[0][2] = temp[2] * axis[0] + s * axis[1]; - - rotation[1][0] = temp[0] * axis[1] + s * axis[2]; - rotation[1][1] = c + temp[1] * axis[1]; - rotation[1][2] = temp[2] * axis[1] - s * axis[0]; - - rotation[2][0] = temp[0] * axis[2] - s * axis[1]; - rotation[2][1] = temp[1] * axis[2] + s * axis[0]; - rotation[2][2] = c + temp[2] * axis[2]; - - return rotation; -} - -namespace impl -{ -template -struct zero_expand_helper -{ - static vector __call(const vector inVec) - { - return vector(inVec, vector(0)); - } -}; -template -struct zero_expand_helper -{ - static vector __call(const vector inVec) - { - return inVec; - } -}; -} - -template= MIn) -vector zero_expand(vector inVec) -{ - return impl::zero_expand_helper::__call(inVec); + return r; } -template = NIn && MOut >= MIn) -matrix promote_affine(const matrix inMatrix) +template +inline matrix rhLookAt( + const vector& position, + const vector& target, + const vector& upVector) { - matrix retval; + const vector zaxis = hlsl::normalize(position - target); + const vector xaxis = hlsl::normalize(hlsl::cross(upVector, zaxis)); + const vector yaxis = hlsl::cross(zaxis, xaxis); - using out_row_t = hlsl::vector; + matrix r; + r[0] = vector(xaxis, -hlsl::dot(xaxis, position)); + r[1] = vector(yaxis, -hlsl::dot(yaxis, position)); + r[2] = vector(zaxis, -hlsl::dot(zaxis, position)); - NBL_UNROLL for (uint32_t row_i = 0; row_i < NIn; row_i++) - { - retval[row_i] = zero_expand(inMatrix[row_i]); - } - NBL_UNROLL for (uint32_t row_i = NIn; row_i < NOut; row_i++) - { - retval[row_i] = promote(0.0); - if (row_i >= MIn && row_i < MOut) - retval[row_i][row_i] = T(1.0); - } - return retval; + return r; } } diff --git a/include/nbl/builtin/hlsl/math/octahedral.hlsl b/include/nbl/builtin/hlsl/math/octahedral.hlsl new file mode 100644 index 0000000000..5f12fc79c0 --- /dev/null +++ b/include/nbl/builtin/hlsl/math/octahedral.hlsl @@ -0,0 +1,81 @@ +// 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_BUILTIN_HLSL_MATH_OCTAHEDRAL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_OCTAHEDRAL_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/numbers.hlsl" +#include "nbl/builtin/hlsl/math/functions.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ + +// Octahedral Transform, maps 3D direction vectors to 2D square and vice versa +template +struct OctahedralTransform +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + + // F : [-1, 1]^2 -> S^2 + static vector3_type ndcToDir(NBL_CONST_REF_ARG(vector2_type) ndc) + { + const vector2_type a = abs(ndc); + vector3_type p = vector3_type(ndc, scalar_type(1) - a.x - a.y); + + if (p.z < scalar_type(0)) + p.xy = __foldToUpperHemisphere(ndc); + + return hlsl::normalize(p); + } + + // F : [0, 1]^2 -> S^2 (UV with half-texel handling) + static vector3_type uvToDir(NBL_CONST_REF_ARG(vector2_type) uv, NBL_CONST_REF_ARG(vector2_type) halfMinusHalfPixel) + { + const vector2_type ndc = (uv - vector2_type(scalar_type(0.5), scalar_type(0.5))) / halfMinusHalfPixel; + return ndcToDir(ndc); + } + + // F^-1 : S^2 -> [-1, 1]^2 + static vector2_type dirToNDC(NBL_CONST_REF_ARG(vector3_type) d) + { + const scalar_type sum = lpNorm(d); + vector3_type s = d / sum; + + if (s.z < scalar_type(0)) + s.xy = __foldToUpperHemisphere(s.xy); + + return s.xy; + } + + // transforms direction vector into UV with half-texel handling + // dir in S^2, halfMinusHalfPixel in [0, 0.5)^2, + // where halfMinusHalfPixel = 0.5-0.5/texSize + static vector2_type dirToUV(NBL_CONST_REF_ARG(vector3_type) dir, NBL_CONST_REF_ARG(vector2_type) halfMinusHalfPixel) + { + return dirToNDC(dir) * halfMinusHalfPixel + scalar_type(0.5); + } + + static vector2_type __foldToUpperHemisphere(NBL_CONST_REF_ARG(vector2_type) v) + { + // Use copySign instead of sign() to preserve -0 and avoid DXC corner cases. + const vector2_type factor = vector2_type( + ieee754::copySign(scalar_type(1), v.x), + ieee754::copySign(scalar_type(1), v.y)); + const vector2_type swapped = vector2_type(v.y, v.x); + return factor * (vector2_type(scalar_type(1), scalar_type(1)) - abs(swapped)); + } +}; + +} +} +} + +#endif // _NBL_BUILTIN_HLSL_MATH_OCTAHEDRAL_INCLUDED_ diff --git a/include/nbl/builtin/hlsl/math/polar.hlsl b/include/nbl/builtin/hlsl/math/polar.hlsl index 7b30e3bb8f..98d37a3978 100644 --- a/include/nbl/builtin/hlsl/math/polar.hlsl +++ b/include/nbl/builtin/hlsl/math/polar.hlsl @@ -14,27 +14,29 @@ namespace hlsl namespace math { -template +template struct Polar { using scalar_type = T; using vector2_type = vector; using vector3_type = vector; - // should be normalized - static Polar createFromCartesian(const vector3_type coords) + // input must be normalized + static Polar createFromCartesian(const vector3_type dir) { Polar retval; - retval.theta = hlsl::acos(coords.z); - retval.phi = hlsl::atan2(coords.y, coords.x); + retval.theta = hlsl::acos(dir.z); + retval.phi = hlsl::atan2(dir.y, dir.x); return retval; } static vector3_type ToCartesian(const scalar_type theta, const scalar_type phi) { - return vector(hlsl::cos(phi) * hlsl::cos(theta), - hlsl::sin(phi) * hlsl::cos(theta), - hlsl::sin(theta)); + return vector( + hlsl::cos(phi) * hlsl::cos(theta), + hlsl::sin(phi) * hlsl::cos(theta), + hlsl::sin(theta) + ); } vector3_type getCartesian() @@ -42,8 +44,8 @@ struct Polar return ToCartesian(theta, phi); } - scalar_type theta; - scalar_type phi; + scalar_type theta; //! polar angle + scalar_type phi; //! azimuthal angle }; } diff --git a/include/nbl/builtin/hlsl/math/quadrature/gauss_legendre/impl.hlsl b/include/nbl/builtin/hlsl/math/quadrature/gauss_legendre/impl.hlsl index 3bcfbb2388..cd402d0cd4 100644 --- a/include/nbl/builtin/hlsl/math/quadrature/gauss_legendre/impl.hlsl +++ b/include/nbl/builtin/hlsl/math/quadrature/gauss_legendre/impl.hlsl @@ -14,25 +14,25 @@ namespace float_t_namespace { -NBL_CONSTEXPR float_t xi_2[2] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_2[2] = { TYPED_NUMBER(-0.5773502691896257), TYPED_NUMBER(0.5773502691896257) }; -NBL_CONSTEXPR float_t xi_3[3] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_3[3] = { TYPED_NUMBER(0.0), TYPED_NUMBER(-0.7745966692414833), TYPED_NUMBER(0.7745966692414833) }; -NBL_CONSTEXPR float_t xi_4[4] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_4[4] = { TYPED_NUMBER(-0.3399810435848562), TYPED_NUMBER(0.3399810435848562), TYPED_NUMBER(-0.8611363115940525), TYPED_NUMBER(0.8611363115940525) }; -NBL_CONSTEXPR float_t xi_5[5] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_5[5] = { TYPED_NUMBER(0.0), TYPED_NUMBER(-0.5384693101056830), TYPED_NUMBER(0.5384693101056830), @@ -40,7 +40,7 @@ NBL_CONSTEXPR float_t xi_5[5] = { TYPED_NUMBER(0.9061798459386639) }; -NBL_CONSTEXPR float_t xi_6[6] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_6[6] = { TYPED_NUMBER(0.6612093864662645), TYPED_NUMBER(-0.6612093864662645), TYPED_NUMBER(-0.2386191860831969), @@ -49,7 +49,7 @@ NBL_CONSTEXPR float_t xi_6[6] = { TYPED_NUMBER(0.9324695142031520) }; -NBL_CONSTEXPR float_t xi_7[7] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_7[7] = { TYPED_NUMBER(0.0), TYPED_NUMBER(0.4058451513773971), TYPED_NUMBER(-0.4058451513773971), @@ -59,7 +59,7 @@ NBL_CONSTEXPR float_t xi_7[7] = { TYPED_NUMBER(0.9491079123427585) }; -NBL_CONSTEXPR float_t xi_8[8] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_8[8] = { TYPED_NUMBER(-0.1834346424956498), TYPED_NUMBER(0.1834346424956498), TYPED_NUMBER(-0.5255324099163289), @@ -70,7 +70,7 @@ NBL_CONSTEXPR float_t xi_8[8] = { TYPED_NUMBER(0.9602898564975362) }; -NBL_CONSTEXPR float_t xi_9[9] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_9[9] = { TYPED_NUMBER(0.0), TYPED_NUMBER(-0.8360311073266357), TYPED_NUMBER(0.8360311073266357), @@ -82,7 +82,7 @@ NBL_CONSTEXPR float_t xi_9[9] = { TYPED_NUMBER(0.6133714327005903) }; -NBL_CONSTEXPR float_t xi_10[10] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_10[10] = { TYPED_NUMBER(-0.1488743389816312), TYPED_NUMBER(0.1488743389816312), TYPED_NUMBER(-0.4333953941292471), @@ -95,7 +95,7 @@ NBL_CONSTEXPR float_t xi_10[10] = { TYPED_NUMBER(0.9739065285171717) }; -NBL_CONSTEXPR float_t xi_11[11] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_11[11] = { TYPED_NUMBER(0.0), TYPED_NUMBER(-0.2695431559523449), TYPED_NUMBER(0.2695431559523449), @@ -109,7 +109,7 @@ NBL_CONSTEXPR float_t xi_11[11] = { TYPED_NUMBER(0.9782286581460569) }; -NBL_CONSTEXPR float_t xi_12[12] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_12[12] = { TYPED_NUMBER(-0.1252334085114689), TYPED_NUMBER(0.1252334085114689), TYPED_NUMBER(-0.3678314989981801), @@ -124,7 +124,7 @@ NBL_CONSTEXPR float_t xi_12[12] = { TYPED_NUMBER(0.9815606342467192) }; -NBL_CONSTEXPR float_t xi_13[13] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_13[13] = { TYPED_NUMBER(0.0), TYPED_NUMBER(-0.2304583159551347), TYPED_NUMBER(0.2304583159551347), @@ -140,7 +140,7 @@ NBL_CONSTEXPR float_t xi_13[13] = { TYPED_NUMBER(0.9841830547185881) }; -NBL_CONSTEXPR float_t xi_14[14] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_14[14] = { TYPED_NUMBER(-0.1080549487073436), TYPED_NUMBER(0.1080549487073436), TYPED_NUMBER(-0.3191123689278897), @@ -157,7 +157,7 @@ NBL_CONSTEXPR float_t xi_14[14] = { TYPED_NUMBER(0.9862838086968123) }; -NBL_CONSTEXPR float_t xi_15[15] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t xi_15[15] = { TYPED_NUMBER(0.0), TYPED_NUMBER(-0.2011940939974345), TYPED_NUMBER(0.2011940939974345), @@ -175,25 +175,25 @@ NBL_CONSTEXPR float_t xi_15[15] = { TYPED_NUMBER(0.9879925180204854) }; -NBL_CONSTEXPR float_t wi_2[2] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_2[2] = { TYPED_NUMBER(1.0000000000000000), TYPED_NUMBER(1.0000000000000000) }; -NBL_CONSTEXPR float_t wi_3[3] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_3[3] = { TYPED_NUMBER(0.8888888888888888), TYPED_NUMBER(0.5555555555555555), TYPED_NUMBER(0.5555555555555555) }; -NBL_CONSTEXPR float_t wi_4[4] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_4[4] = { TYPED_NUMBER(0.6521451548625461), TYPED_NUMBER(0.6521451548625461), TYPED_NUMBER(0.3478548451374538), TYPED_NUMBER(0.3478548451374538) }; -NBL_CONSTEXPR float_t wi_5[5] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_5[5] = { TYPED_NUMBER(0.5688888888888888), TYPED_NUMBER(0.4786286704993664), TYPED_NUMBER(0.4786286704993664), @@ -201,7 +201,7 @@ NBL_CONSTEXPR float_t wi_5[5] = { TYPED_NUMBER(0.2369268850561890) }; -NBL_CONSTEXPR float_t wi_6[6] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_6[6] = { TYPED_NUMBER(0.3607615730481386), TYPED_NUMBER(0.3607615730481386), TYPED_NUMBER(0.4679139345726910), @@ -210,7 +210,7 @@ NBL_CONSTEXPR float_t wi_6[6] = { TYPED_NUMBER(0.1713244923791703) }; -NBL_CONSTEXPR float_t wi_7[7] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_7[7] = { TYPED_NUMBER(0.4179591836734693), TYPED_NUMBER(0.3818300505051189), TYPED_NUMBER(0.3818300505051189), @@ -220,7 +220,7 @@ NBL_CONSTEXPR float_t wi_7[7] = { TYPED_NUMBER(0.1294849661688696) }; -NBL_CONSTEXPR float_t wi_8[8] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_8[8] = { TYPED_NUMBER(0.3626837833783619), TYPED_NUMBER(0.3626837833783619), TYPED_NUMBER(0.3137066458778872), @@ -231,7 +231,7 @@ NBL_CONSTEXPR float_t wi_8[8] = { TYPED_NUMBER(0.1012285362903762) }; -NBL_CONSTEXPR float_t wi_9[9] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_9[9] = { TYPED_NUMBER(0.3302393550012597), TYPED_NUMBER(0.1806481606948574), TYPED_NUMBER(0.1806481606948574), @@ -243,7 +243,7 @@ NBL_CONSTEXPR float_t wi_9[9] = { TYPED_NUMBER(0.2606106964029354) }; -NBL_CONSTEXPR float_t wi_10[10] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_10[10] = { TYPED_NUMBER(0.2955242247147528), TYPED_NUMBER(0.2955242247147528), TYPED_NUMBER(0.2692667193099963), @@ -256,7 +256,7 @@ NBL_CONSTEXPR float_t wi_10[10] = { TYPED_NUMBER(0.0666713443086881) }; -NBL_CONSTEXPR float_t wi_11[11] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_11[11] = { TYPED_NUMBER(0.2729250867779006), TYPED_NUMBER(0.2628045445102466), TYPED_NUMBER(0.2628045445102466), @@ -270,7 +270,7 @@ NBL_CONSTEXPR float_t wi_11[11] = { TYPED_NUMBER(0.0556685671161736) }; -NBL_CONSTEXPR float_t wi_12[12] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_12[12] = { TYPED_NUMBER(0.2491470458134027), TYPED_NUMBER(0.2491470458134027), TYPED_NUMBER(0.2334925365383548), @@ -285,7 +285,7 @@ NBL_CONSTEXPR float_t wi_12[12] = { TYPED_NUMBER(0.0471753363865118) }; -NBL_CONSTEXPR float_t wi_13[13] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_13[13] = { TYPED_NUMBER(0.2325515532308739), TYPED_NUMBER(0.2262831802628972), TYPED_NUMBER(0.2262831802628972), @@ -301,7 +301,7 @@ NBL_CONSTEXPR float_t wi_13[13] = { TYPED_NUMBER(0.0404840047653158) }; -NBL_CONSTEXPR float_t wi_14[14] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_14[14] = { TYPED_NUMBER(0.2152638534631577), TYPED_NUMBER(0.2152638534631577), TYPED_NUMBER(0.2051984637212956), @@ -318,7 +318,7 @@ NBL_CONSTEXPR float_t wi_14[14] = { TYPED_NUMBER(0.0351194603317518) }; -NBL_CONSTEXPR float_t wi_15[15] = { +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t wi_15[15] = { TYPED_NUMBER(0.2025782419255612), TYPED_NUMBER(0.1984314853271115), TYPED_NUMBER(0.1984314853271115), diff --git a/include/nbl/builtin/hlsl/math/quaternions.hlsl b/include/nbl/builtin/hlsl/math/quaternions.hlsl index aca8d1ff3c..49a8f95d22 100644 --- a/include/nbl/builtin/hlsl/math/quaternions.hlsl +++ b/include/nbl/builtin/hlsl/math/quaternions.hlsl @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2023 - 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_BUILTIN_HLSL_MATH_QUATERNIONS_INCLUDED_ @@ -6,6 +6,7 @@ #include "nbl/builtin/hlsl/cpp_compat.hlsl" #include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/matrix_utils/matrix_runtime_traits.hlsl" namespace nbl { @@ -14,37 +15,219 @@ namespace hlsl namespace math { +template +struct truncated_quaternion +{ + using this_t = truncated_quaternion; + using scalar_type = T; + using data_type = vector; + + static this_t create() + { + this_t q; + q.data = data_type(0.0, 0.0, 0.0); + return q; + } + + data_type data; +}; + template -struct quaternion_t +struct quaternion { - using this_t = quaternion_t; + using this_t = quaternion; using scalar_type = T; using data_type = vector; using vector3_type = vector; using matrix_type = matrix; - static this_t createFromTruncated(const vector3_type first3Components) + using AsUint = typename unsigned_integer_of_size::type; + + static this_t create() + { + this_t q; + q.data = data_type(0.0, 0.0, 0.0, 1.0); + return q; + } + + // angle: Rotation angle expressed in radians. + // axis: Rotation axis, must be normalized. + template) + static this_t create(const U axis, const typename vector_traits::scalar_type angle, const typename vector_traits::scalar_type uniformScale = typename vector_traits::scalar_type(1.0)) + { + using scalar_t = typename vector_traits::scalar_type; + this_t q; + const scalar_t halfAngle = angle * scalar_t(0.5); + const scalar_t sinTheta = hlsl::sin(halfAngle); + const scalar_t cosTheta = hlsl::cos(halfAngle); + q.data = data_type(axis * sinTheta, cosTheta) * uniformScale; + return q; + } + + // applies rotation equivalent to 3x3 matrix in order of pitch * yaw * roll (X * Y * Z) -- mul(roll,mul(yaw,mul(pitch,v))) + template NBL_FUNC_REQUIRES(is_same_v,U>) + static this_t create(const U halfPitchCosSin, const U halfYawCosSin, const U halfRollCosSin) + { + const scalar_type cp = halfPitchCosSin.x; + const scalar_type sp = halfPitchCosSin.y; + + const scalar_type cy = halfYawCosSin.x; + const scalar_type sy = halfYawCosSin.y; + + const scalar_type cr = halfRollCosSin.x; + const scalar_type sr = halfRollCosSin.y; + + this_t q; + q.data[0] = cr * sp * cy + sr * cp * sy; // x + q.data[1] = cr * cp * sy - sr * sp * cy; // y + q.data[2] = sr * cp * cy - cr * sp * sy; // z + q.data[3] = cr * cp * cy + sr * sp * sy; // w + + return q; + } + + template) + static this_t create(const U pitch, const U yaw, const U roll) + { + const scalar_type halfPitch = pitch * scalar_type(0.5); + const scalar_type halfYaw = yaw * scalar_type(0.5); + const scalar_type halfRoll = roll * scalar_type(0.5); + + return create( + vector(hlsl::cos(halfPitch), hlsl::sin(halfPitch)), + vector(hlsl::cos(halfYaw), hlsl::sin(halfYaw)), + vector(hlsl::cos(halfRoll), hlsl::sin(halfRoll)) + ); + } + + static this_t create(NBL_CONST_REF_ARG(matrix_type) _m, const bool dontAssertValidMatrix=false) + { + scalar_type uniformColumnSqNorm; + { + // only orthogonal and uniform scale mats can be converted + linalg::RuntimeTraits traits = linalg::RuntimeTraits::create(_m); + bool valid = traits.orthogonal && !hlsl::isnan(traits.uniformColumnSqNorm); + uniformColumnSqNorm = traits.uniformColumnSqNorm; + + if (dontAssertValidMatrix) + { + if (!valid) + { + this_t retval; + retval.data = hlsl::promote(bit_cast(numeric_limits::quiet_NaN)); + return retval; + } + } + else + assert(valid); + } + if (uniformColumnSqNorm < numeric_limits::min) + { + this_t retval; + retval.data = hlsl::promote(bit_cast(numeric_limits::quiet_NaN)); + return retval; + } + + const scalar_type uniformScale = hlsl::sqrt(uniformColumnSqNorm); + matrix_type m = _m; + m /= uniformScale; + + const scalar_type m00 = m[0][0], m11 = m[1][1], m22 = m[2][2]; + const scalar_type neg_m00 = -m00; + const scalar_type neg_m11 = -m11; + const scalar_type neg_m22 = -m22; + const data_type Qx = data_type(m00, m00, neg_m00, neg_m00); + const data_type Qy = data_type(m11, neg_m11, m11, neg_m11); + const data_type Qz = data_type(m22, neg_m22, neg_m22, m22); + + const data_type tmp = Qx + Qy + Qz; + + // TODO: speed this up + this_t retval; + if (tmp.x > scalar_type(0.0)) + { + const scalar_type scales = hlsl::sqrt(tmp.x + scalar_type(1.0)); + const scalar_type invscales = scalar_type(0.5) / scales; + retval.data.x = (m[2][1] - m[1][2]) * invscales; + retval.data.y = (m[0][2] - m[2][0]) * invscales; + retval.data.z = (m[1][0] - m[0][1]) * invscales; + retval.data.w = scales * scalar_type(0.5); + } + else + { + if (tmp.y > scalar_type(0.0)) + { + const scalar_type scales = hlsl::sqrt(tmp.y + scalar_type(1.0)); + const scalar_type invscales = scalar_type(0.5) / scales; + retval.data.x = scales * scalar_type(0.5); + retval.data.y = (m[0][1] + m[1][0]) * invscales; + retval.data.z = (m[2][0] + m[0][2]) * invscales; + retval.data.w = (m[2][1] - m[1][2]) * invscales; + } + else if (tmp.z > scalar_type(0.0)) + { + const scalar_type scales = hlsl::sqrt(tmp.z + scalar_type(1.0)); + const scalar_type invscales = scalar_type(0.5) / scales; + retval.data.x = (m[0][1] + m[1][0]) * invscales; + retval.data.y = scales * scalar_type(0.5); + retval.data.z = (m[1][2] + m[2][1]) * invscales; + retval.data.w = (m[0][2] - m[2][0]) * invscales; + } + else + { + const scalar_type scales = hlsl::sqrt(tmp.w + scalar_type(1.0)); + const scalar_type invscales = scalar_type(0.5) / scales; + retval.data.x = (m[0][2] + m[2][0]) * invscales; + retval.data.y = (m[1][2] + m[2][1]) * invscales; + retval.data.z = scales * scalar_type(0.5); + retval.data.w = (m[1][0] - m[0][1]) * invscales; + } + } + + retval.data = retval.data * uniformScale; // restore uniform scale + return retval; + } + + this_t operator*(scalar_type scalar) NBL_CONST_MEMBER_FUNC + { + this_t output; + output.data = data * scalar; + return output; + } + + this_t operator*(NBL_CONST_REF_ARG(this_t) other) NBL_CONST_MEMBER_FUNC { this_t retval; - retval.data.xyz = first3Components; - retval.data.w = hlsl::sqrt(scalar_type(1.0) - hlsl::dot(first3Components, first3Components)); + retval.data = data_type( + data.w * other.data.x + data.x * other.data.w + data.y * other.data.z - data.z * other.data.y, + data.w * other.data.y - data.x * other.data.z + data.y * other.data.w + data.z * other.data.x, + data.w * other.data.z + data.x * other.data.y - data.y * other.data.x + data.z * other.data.w, + data.w * other.data.w - data.x * other.data.x - data.y * other.data.y - data.z * other.data.z + ); return retval; } - static this_t lerp(const this_t start, const this_t end, const scalar_type fraction, const scalar_type totalPseudoAngle) + static this_t unnormLerp(const this_t start, const this_t end, const scalar_type fraction, const scalar_type totalPseudoAngle) { - using AsUint = typename unsigned_integer_of_size::type; - const AsUint negationMask = hlsl::bit_cast(totalPseudoAngle) & AsUint(0x80000000u); - const data_type adjEnd = hlsl::bit_cast(hlsl::bit_cast(end.data) ^ negationMask); + assert(testing::relativeApproxCompare(hlsl::length(start.data), scalar_type(1.0), scalar_type(1e-4))); + assert(testing::relativeApproxCompare(hlsl::length(end.data), scalar_type(1.0), scalar_type(1e-4))); + const data_type adjEnd = ieee754::flipSignIfRHSNegative(end.data, totalPseudoAngle); this_t retval; - retval.data = hlsl::mix(start.data, adjEnd, fraction); + retval.data = hlsl::mix(start.data, adjEnd, hlsl::promote(fraction)); return retval; } + static this_t unnormLerp(const this_t start, const this_t end, const scalar_type fraction) + { + return unnormLerp(start, end, fraction, hlsl::dot(start.data, end.data)); + } + static this_t lerp(const this_t start, const this_t end, const scalar_type fraction) { - return lerp(start, end, fraction, hlsl::dot(start.data, end.data)); + this_t retval = unnormLerp(start, end, fraction); + retval.data = hlsl::normalize(retval.data); + return retval; } static scalar_type __adj_interpolant(const scalar_type angle, const scalar_type fraction, const scalar_type interpolantPrecalcTerm2, const scalar_type interpolantPrecalcTerm3) @@ -55,19 +238,43 @@ struct quaternion_t return fraction + interpolantPrecalcTerm3 * k; } - static this_t flerp(const this_t start, const this_t end, const scalar_type fraction) + static this_t unnormFlerp(const this_t start, const this_t end, const scalar_type fraction) { + assert(testing::relativeApproxCompare(hlsl::length(start.data), scalar_type(1.0), scalar_type(1e-4))); + assert(testing::relativeApproxCompare(hlsl::length(end.data), scalar_type(1.0), scalar_type(1e-4))); + const scalar_type pseudoAngle = hlsl::dot(start.data,end.data); const scalar_type interpolantPrecalcTerm = fraction - scalar_type(0.5); const scalar_type interpolantPrecalcTerm3 = fraction * interpolantPrecalcTerm * (fraction - scalar_type(1.0)); const scalar_type adjFrac = __adj_interpolant(hlsl::abs(pseudoAngle),fraction,interpolantPrecalcTerm*interpolantPrecalcTerm,interpolantPrecalcTerm3); - this_t retval = lerp(start,end,adjFrac,pseudoAngle); + this_t retval = unnormLerp(start,end,adjFrac,pseudoAngle); + return retval; + } + + static this_t flerp(const this_t start, const this_t end, const scalar_type fraction) + { + this_t retval = unnormFlerp(start,end,fraction); retval.data = hlsl::normalize(retval.data); return retval; } - matrix_type constructMatrix() + vector3_type transformVector(const vector3_type v, const bool assumeNoScale=false) NBL_CONST_MEMBER_FUNC + { + const scalar_type scaleRcp = hlsl::rsqrt(hlsl::dot(data, data)); + vector3_type retV = v; + scalar_type modVScale = scalar_type(2.0); + if (!assumeNoScale) + { + retV /= scaleRcp; + modVScale *= scaleRcp; + } + const vector3_type modV = v * modVScale; + const vector3_type direction = data.xyz; + return retV + hlsl::cross(direction, modV * data.w + hlsl::cross(direction, modV)); + } + + matrix_type __constructMatrix() NBL_CONST_MEMBER_FUNC { matrix_type mat; mat[0] = data.yzx * data.ywz + data.zxy * data.zyw * vector3_type( 1.0, 1.0,-1.0); @@ -76,8 +283,11 @@ struct quaternion_t mat[0][0] = scalar_type(0.5) - mat[0][0]; mat[1][1] = scalar_type(0.5) - mat[1][1]; mat[2][2] = scalar_type(0.5) - mat[2][2]; - mat *= scalar_type(2.0); - return hlsl::transpose(mat); // TODO: double check transpose? + mat[0] = mat[0] * scalar_type(2.0); + mat[1] = mat[1] * scalar_type(2.0); + mat[2] = mat[2] * scalar_type(2.0); + + return hlsl::transpose(mat); } static vector3_type slerp_delta(const vector3_type start, const vector3_type preScaledWaypoint, scalar_type cosAngleFromStart) @@ -94,10 +304,110 @@ struct quaternion_t return precompPart * cosAngle + hlsl::cross(planeNormal, precompPart); } + static this_t slerp(const this_t start, const this_t end, const scalar_type fraction, const scalar_type threshold = numeric_limits::epsilon) + { + const scalar_type totalPseudoAngle = hlsl::dot(start.data, end.data); + + // make sure we use the short rotation + const scalar_type cosA = ieee754::flipSignIfRHSNegative(totalPseudoAngle, totalPseudoAngle); + if (cosA <= (scalar_type(1.0) - threshold)) // spherical interpolation + { + assert(testing::relativeApproxCompare(hlsl::length(start.data), scalar_type(1.0), scalar_type(1e-4))); + assert(testing::relativeApproxCompare(hlsl::length(end.data), scalar_type(1.0), scalar_type(1e-4))); + + this_t retval; + const scalar_type sinARcp = scalar_type(1.0) / hlsl::sqrt(scalar_type(1.0) - cosA * cosA); + const scalar_type sinAt = hlsl::sin(fraction * hlsl::acos(cosA)); + const scalar_type sinAt_over_sinA = sinAt*sinARcp; + const scalar_type scale = hlsl::sqrt(scalar_type(1.0)-sinAt*sinAt) - sinAt_over_sinA*cosA; //cosAt-cos(A)sin(tA)/sin(A) = (sin(A)cos(tA)-cos(A)sin(tA))/sin(A) + const data_type adjEnd = ieee754::flipSignIfRHSNegative(end.data, totalPseudoAngle); + retval.data = scale * start.data + sinAt_over_sinA * adjEnd; + + return retval; + } + else + return unnormLerp(start, end, fraction, totalPseudoAngle); + } + data_type data; }; } + + +namespace cpp_compat_intrinsics_impl +{ +template +struct normalize_helper > +{ + static inline math::quaternion __call(const math::quaternion q) + { + math::quaternion retval; + retval.data = hlsl::normalize(q.data); + return retval; + } +}; +} + +namespace impl +{ +template +struct static_cast_helper, math::truncated_quaternion > +{ + static inline math::quaternion cast(const math::truncated_quaternion q) + { + math::quaternion retval; + retval.data.xyz = q.data; + retval.data.w = hlsl::sqrt(T(1.0) - hlsl::dot(q.data, q.data)); + return retval; + } +}; + +template +struct static_cast_helper, math::quaternion > +{ + static inline math::truncated_quaternion cast(const math::quaternion q) + { + assert(testing::relativeApproxCompare(hlsl::length(q.data), T(1.0), T(1e-4))); + math::truncated_quaternion t; + t.data.x = q.data.x; + t.data.y = q.data.y; + t.data.z = q.data.z; + return t; + } +}; + +template +struct static_cast_helper, math::quaternion > +{ + static inline matrix cast(const math::quaternion q) + { + return q.__constructMatrix(); + } +}; + +template +struct static_cast_helper, matrix > +{ + static inline math::quaternion cast(const matrix m) + { + return math::quaternion::create(m, true); + } +}; +} + +template +math::quaternion inverse(const math::quaternion q) +{ + math::quaternion retval; + retval.data.x = -q.data.x; + retval.data.y = -q.data.y; + retval.data.z = -q.data.z; + retval.data.w = q.data.w; + retval.data /= hlsl::dot(q.data,q.data); + return retval; +} + } } diff --git a/include/nbl/builtin/hlsl/math/thin_lens_projection.hlsl b/include/nbl/builtin/hlsl/math/thin_lens_projection.hlsl new file mode 100644 index 0000000000..985ec8a6a3 --- /dev/null +++ b/include/nbl/builtin/hlsl/math/thin_lens_projection.hlsl @@ -0,0 +1,111 @@ +#ifndef _NBL_BUILTIN_HLSL_MATH_THIN_LENS_PROJECTION_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_THIN_LENS_PROJECTION_INCLUDED_ + +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ +namespace thin_lens +{ + +template) +inline matrix rhPerspectiveFovMatrix(FloatingPoint fieldOfViewRadians, FloatingPoint aspectRatio, FloatingPoint zNear, FloatingPoint zFar) +{ + const FloatingPoint h = core::reciprocal(tan(fieldOfViewRadians * 0.5f)); + _NBL_DEBUG_BREAK_IF(aspectRatio == 0.f); //division by zero + const float w = h / aspectRatio; + + _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero + + matrix m; + m[0] = vector(w, 0.f, 0.f, 0.f); + m[1] = vector(0.f, -h, 0.f, 0.f); + m[2] = vector(0.f, 0.f, -zFar / (zFar - zNear), -zNear * zFar / (zFar - zNear)); + m[3] = vector(0.f, 0.f, -1.f, 0.f); + + return m; +} +template) +inline matrix lhPerspectiveFovMatrix(FloatingPoint fieldOfViewRadians, FloatingPoint aspectRatio, FloatingPoint zNear, FloatingPoint zFar) +{ + const FloatingPoint h = core::reciprocal(tan(fieldOfViewRadians * 0.5f)); + _NBL_DEBUG_BREAK_IF(aspectRatio == 0.f); //division by zero + const float w = h / aspectRatio; + + _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero + + matrix m; + m[0] = vector(w, 0.f, 0.f, 0.f); + m[1] = vector(0.f, -h, 0.f, 0.f); + m[2] = vector(0.f, 0.f, zFar / (zFar - zNear), -zNear * zFar / (zFar - zNear)); + m[3] = vector(0.f, 0.f, 1.f, 0.f); + + return m; +} + +template) +inline matrix rhProjectionOrthoMatrix(FloatingPoint widthOfViewVolume, FloatingPoint heightOfViewVolume, FloatingPoint zNear, FloatingPoint zFar) +{ + _NBL_DEBUG_BREAK_IF(widthOfViewVolume == 0.f); //division by zero + _NBL_DEBUG_BREAK_IF(heightOfViewVolume == 0.f); //division by zero + _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero + + matrix m; + m[0] = vector(2.f / widthOfViewVolume, 0.f, 0.f, 0.f); + m[1] = vector(0.f, -2.f / heightOfViewVolume, 0.f, 0.f); + m[2] = vector(0.f, 0.f, -1.f / (zFar - zNear), -zNear / (zFar - zNear)); + m[3] = vector(0.f, 0.f, 0.f, 1.f); + + return m; +} + +template) +inline matrix lhProjectionOrthoMatrix(FloatingPoint widthOfViewVolume, FloatingPoint heightOfViewVolume, FloatingPoint zNear, FloatingPoint zFar) +{ + _NBL_DEBUG_BREAK_IF(widthOfViewVolume == 0.f); //division by zero + _NBL_DEBUG_BREAK_IF(heightOfViewVolume == 0.f); //division by zero + _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero + + matrix m; + m[0] = vector(2.f / widthOfViewVolume, 0.f, 0.f, 0.f); + m[1] = vector(0.f, -2.f / heightOfViewVolume, 0.f, 0.f); + m[2] = vector(0.f, 0.f, 1.f / (zFar - zNear), -zNear / (zFar - zNear)); + m[3] = vector(0.f, 0.f, 0.f, 1.f); + + return m; +} + +} +} + +template) +inline matrix buildProjectionMatrixPerspectiveFovRH(FloatingPoint fieldOfViewRadians, FloatingPoint aspectRatio, FloatingPoint zNear, FloatingPoint zFar) +{ + return math::thin_lens::rhPerspectiveFovMatrix(fieldOfViewRadians, aspectRatio, zNear, zFar); +} +template) +inline matrix buildProjectionMatrixPerspectiveFovLH(FloatingPoint fieldOfViewRadians, FloatingPoint aspectRatio, FloatingPoint zNear, FloatingPoint zFar) +{ + return math::thin_lens::lhPerspectiveFovMatrix(fieldOfViewRadians, aspectRatio, zNear, zFar); +} + +template) +inline matrix buildProjectionMatrixOrthoRH(FloatingPoint widthOfViewVolume, FloatingPoint heightOfViewVolume, FloatingPoint zNear, FloatingPoint zFar) +{ + return math::thin_lens::rhProjectionOrthoMatrix(widthOfViewVolume, heightOfViewVolume, zNear, zFar); +} +template) +inline matrix buildProjectionMatrixOrthoLH(FloatingPoint widthOfViewVolume, FloatingPoint heightOfViewVolume, FloatingPoint zNear, FloatingPoint zFar) +{ + return math::thin_lens::lhProjectionOrthoMatrix(widthOfViewVolume, heightOfViewVolume, zNear, zFar); +} + +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/matrix_utils/matrix_runtime_traits.hlsl b/include/nbl/builtin/hlsl/matrix_utils/matrix_runtime_traits.hlsl new file mode 100644 index 0000000000..02f28dafde --- /dev/null +++ b/include/nbl/builtin/hlsl/matrix_utils/matrix_runtime_traits.hlsl @@ -0,0 +1,68 @@ +// 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_BUILTIN_HLSL_MATRIX_UTILS_MATRIX_RUNTIME_TRAITS_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATRIX_UTILS_MATRIX_RUNTIME_TRAITS_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/testing/relative_approx_compare.hlsl" +#include "nbl/builtin/hlsl/concepts/matrix.hlsl" +#include "nbl/builtin/hlsl/matrix_utils/matrix_traits.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ +namespace linalg +{ + +template && matrix_traits::Square) +struct RuntimeTraits +{ + using matrix_t = T; + using scalar_t = typename matrix_traits::scalar_type; + NBL_CONSTEXPR_STATIC_INLINE uint16_t N = matrix_traits::RowCount; + + static RuntimeTraits create(const matrix_t m) + { + RuntimeTraits retval; + retval.invertible = !testing::relativeApproxCompare(hlsl::determinant(m), scalar_t(0.0), 1e-5); + { + bool orthogonal = true; + NBL_UNROLL for (uint16_t i = 0; i < N; i++) + orthogonal = orthogonal && testing::relativeApproxCompare(hlsl::dot(m[i], m[(i+1)%N]), scalar_t(0.0), 1e-4); + retval.orthogonal = orthogonal; + } + { + const matrix_t m_T = hlsl::transpose(m); + scalar_t uniformColumnSqNorm = hlsl::dot(m_T[0], m_T[0]); + NBL_UNROLL for (uint16_t i = 1; i < N; i++) + { + if (!testing::relativeApproxCompare(hlsl::dot(m_T[i], m_T[i]), uniformColumnSqNorm, 1e-4)) + { + uniformColumnSqNorm = bit_cast(numeric_limits::quiet_NaN); + break; + } + } + + retval.uniformColumnSqNorm = uniformColumnSqNorm; + retval.orthonormal = retval.orthogonal && testing::relativeApproxCompare(uniformColumnSqNorm, scalar_t(1.0), 1e-5); + } + return retval; + } + + bool invertible; + bool orthogonal; + scalar_t uniformColumnSqNorm; + bool orthonormal; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/matrix_utils/matrix_traits.hlsl b/include/nbl/builtin/hlsl/matrix_utils/matrix_traits.hlsl index f9c031c8e7..f554be7abe 100644 --- a/include/nbl/builtin/hlsl/matrix_utils/matrix_traits.hlsl +++ b/include/nbl/builtin/hlsl/matrix_utils/matrix_traits.hlsl @@ -21,48 +21,18 @@ struct matrix_traits NBL_CONSTEXPR_STATIC_INLINE bool IsMatrix = false; }; -// i choose to implement it this way because of this DXC bug: https://github.com/microsoft/DirectXShaderCompiler/issues/7007 -#define DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(ROW_COUNT, COLUMN_COUNT) \ -template \ -struct matrix_traits > \ -{ \ - using scalar_type = T; \ - using row_type = vector; \ - using transposed_type = matrix; \ - NBL_CONSTEXPR_STATIC_INLINE uint32_t RowCount = ROW_COUNT; \ - NBL_CONSTEXPR_STATIC_INLINE uint32_t ColumnCount = COLUMN_COUNT; \ - NBL_CONSTEXPR_STATIC_INLINE bool Square = RowCount == ColumnCount; \ - NBL_CONSTEXPR_STATIC_INLINE bool IsMatrix = true; \ -}; - -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(1, 2) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(1, 3) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(1, 4) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(2, 1) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(2, 2) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(2, 3) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(2, 4) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(3, 1) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(3, 2) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(3, 3) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(3, 4) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(4, 1) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(4, 2) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(4, 3) -DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION(4, 4) - -#undef DEFINE_MATRIX_TRAITS_TEMPLATE_SPECIALIZATION - // TODO: when this bug: https://github.com/microsoft/DirectXShaderCompiler/issues/7007 is fixed, uncomment and delete template specializations -/*template +template struct matrix_traits > { using scalar_type = T; - NBL_CONSTEXPR_STATIC_INLINE uint32_t RowCount = ROW_COUNT; - NBL_CONSTEXPR_STATIC_INLINE uint32_t ColumnCount = COLUMN_COUNT; + using row_type = vector; + using transposed_type = matrix; + NBL_CONSTEXPR_STATIC_INLINE uint32_t RowCount = N; + NBL_CONSTEXPR_STATIC_INLINE uint32_t ColumnCount = M; NBL_CONSTEXPR_STATIC_INLINE bool Square = RowCount == ColumnCount; + NBL_CONSTEXPR_STATIC_INLINE bool IsMatrix = true; }; -*/ } } diff --git a/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl b/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl deleted file mode 100644 index 1ad16dc28d..0000000000 --- a/include/nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl +++ /dev/null @@ -1,235 +0,0 @@ -#ifndef _NBL_BUILTIN_HLSL_TRANSFORMATION_MATRIX_UTILS_INCLUDED_ -#define _NBL_BUILTIN_HLSL_TRANSFORMATION_MATRIX_UTILS_INCLUDED_ - -#include - -namespace nbl -{ -namespace hlsl -{ -//TODO: stolen from cameraz branch, don't have epsilonEqual here, maybe uncomment when merging from imguizmo-lights branch -//// TODO: -> move somewhere else and nbl:: to implement it -//template -//bool isOrthoBase(const T& x, const T& y, const T& z, const E epsilon = 1e-6) -//{ -// auto isNormalized = [](const auto& v, const auto& epsilon) -> bool -// { -// return glm::epsilonEqual(glm::length(v), 1.0, epsilon); -// }; -// -// auto isOrthogonal = [](const auto& a, const auto& b, const auto& epsilon) -> bool -// { -// return glm::epsilonEqual(glm::dot(a, b), 0.0, epsilon); -// }; -// -// return isNormalized(x, epsilon) && isNormalized(y, epsilon) && isNormalized(z, epsilon) && -// isOrthogonal(x, y, epsilon) && isOrthogonal(x, z, epsilon) && isOrthogonal(y, z, epsilon); -//} -//// <- - -template -matrix getMatrix3x4As4x4(const matrix& mat) -{ - matrix output; - for (int i = 0; i < 3; ++i) - output[i] = mat[i]; - output[3] = float32_t4(0.0f, 0.0f, 0.0f, 1.0f); - - return output; -} - -template -matrix getMatrix3x3As4x4(const matrix& mat) -{ - matrix output; - for (int i = 0; i < 3; ++i) - output[i] = float32_t4(mat[i], 1.0f); - output[3] = float32_t4(0.0f, 0.0f, 0.0f, 1.0f); - - return output; -} - -template -inline vector getCastedVector(const vector& in) -{ - vector out; - - for (int i = 0; i < N; ++i) - out[i] = (Tout)(in[i]); - - return out; -} - -template -inline matrix getCastedMatrix(const matrix& in) -{ - matrix out; - - for (int i = 0; i < N; ++i) - out[i] = getCastedVector(in[i]); - - return out; -} - -// TODO: use portable_float when merged -//! multiplies matrices a and b, 3x4 matrices are treated as 4x4 matrices with 4th row set to (0, 0, 0 ,1) -template -inline matrix concatenateBFollowedByA(const matrix& a, const matrix& b) -{ - const auto a4x4 = getMatrix3x4As4x4(a); - const auto b4x4 = getMatrix3x4As4x4(b); - return matrix(mul(a4x4, b4x4)); -} - -// /Arek: glm:: for normalize till dot product is fixed (ambiguity with glm namespace + linker issues) - -template -inline matrix buildCameraLookAtMatrixLH( - const vector& position, - const vector& target, - const vector& upVector) -{ - const vector zaxis = glm::normalize(target - position); - const vector xaxis = glm::normalize(hlsl::cross(upVector, zaxis)); - const vector yaxis = hlsl::cross(zaxis, xaxis); - - matrix r; - r[0] = vector(xaxis, -hlsl::dot(xaxis, position)); - r[1] = vector(yaxis, -hlsl::dot(yaxis, position)); - r[2] = vector(zaxis, -hlsl::dot(zaxis, position)); - - return r; -} - -template -inline matrix buildCameraLookAtMatrixRH( - const vector& position, - const vector& target, - const vector& upVector) -{ - const vector zaxis = glm::normalize(position - target); - const vector xaxis = glm::normalize(hlsl::cross(upVector, zaxis)); - const vector yaxis = hlsl::cross(zaxis, xaxis); - - matrix r; - r[0] = vector(xaxis, -hlsl::dot(xaxis, position)); - r[1] = vector(yaxis, -hlsl::dot(yaxis, position)); - r[2] = vector(zaxis, -hlsl::dot(zaxis, position)); - - return r; -} - -// TODO: test, check if there is better implementation -// TODO: move quaternion to nbl::hlsl -// TODO: why NBL_REF_ARG(MatType) doesn't work????? - -//! Replaces curent rocation and scale by rotation represented by quaternion `quat`, leaves 4th row and 4th colum unchanged -template -inline void setRotation(matrix& outMat, NBL_CONST_REF_ARG(core::quaternion) quat) -{ - static_assert(N == 3 || N == 4); - - outMat[0] = vector( - 1 - 2 * (quat.y * quat.y + quat.z * quat.z), - 2 * (quat.x * quat.y - quat.z * quat.w), - 2 * (quat.x * quat.z + quat.y * quat.w), - outMat[0][3] - ); - - outMat[1] = vector( - 2 * (quat.x * quat.y + quat.z * quat.w), - 1 - 2 * (quat.x * quat.x + quat.z * quat.z), - 2 * (quat.y * quat.z - quat.x * quat.w), - outMat[1][3] - ); - - outMat[2] = vector( - 2 * (quat.x * quat.z - quat.y * quat.w), - 2 * (quat.y * quat.z + quat.x * quat.w), - 1 - 2 * (quat.x * quat.x + quat.y * quat.y), - outMat[2][3] - ); -} - -template -inline void setTranslation(matrix& outMat, NBL_CONST_REF_ARG(vector) translation) -{ - static_assert(N == 3 || N == 4); - - outMat[0].w = translation.x; - outMat[1].w = translation.y; - outMat[2].w = translation.z; -} - - -template -inline matrix buildProjectionMatrixPerspectiveFovRH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar) -{ - const float h = core::reciprocal(tanf(fieldOfViewRadians * 0.5f)); - _NBL_DEBUG_BREAK_IF(aspectRatio == 0.f); //division by zero - const float w = h / aspectRatio; - - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix m; - m[0] = vector(w, 0.f, 0.f, 0.f); - m[1] = vector(0.f, -h, 0.f, 0.f); - m[2] = vector(0.f, 0.f, -zFar / (zFar - zNear), -zNear * zFar / (zFar - zNear)); - m[3] = vector(0.f, 0.f, -1.f, 0.f); - - return m; -} -template -inline matrix buildProjectionMatrixPerspectiveFovLH(float fieldOfViewRadians, float aspectRatio, float zNear, float zFar) -{ - const float h = core::reciprocal(tanf(fieldOfViewRadians * 0.5f)); - _NBL_DEBUG_BREAK_IF(aspectRatio == 0.f); //division by zero - const float w = h / aspectRatio; - - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix m; - m[0] = vector(w, 0.f, 0.f, 0.f); - m[1] = vector(0.f, -h, 0.f, 0.f); - m[2] = vector(0.f, 0.f, zFar / (zFar - zNear), -zNear * zFar / (zFar - zNear)); - m[3] = vector(0.f, 0.f, 1.f, 0.f); - - return m; -} - -template -inline matrix buildProjectionMatrixOrthoRH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) -{ - _NBL_DEBUG_BREAK_IF(widthOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(heightOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix m; - m[0] = vector(2.f / widthOfViewVolume, 0.f, 0.f, 0.f); - m[1] = vector(0.f, -2.f / heightOfViewVolume, 0.f, 0.f); - m[2] = vector(0.f, 0.f, -1.f / (zFar - zNear), -zNear / (zFar - zNear)); - m[3] = vector(0.f, 0.f, 0.f, 1.f); - - return m; -} - -template -inline matrix buildProjectionMatrixOrthoLH(float widthOfViewVolume, float heightOfViewVolume, float zNear, float zFar) -{ - _NBL_DEBUG_BREAK_IF(widthOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(heightOfViewVolume == 0.f); //division by zero - _NBL_DEBUG_BREAK_IF(zNear == zFar); //division by zero - - matrix m; - m[0] = vector(2.f / widthOfViewVolume, 0.f, 0.f, 0.f); - m[1] = vector(0.f, -2.f / heightOfViewVolume, 0.f, 0.f); - m[2] = vector(0.f, 0.f, 1.f / (zFar - zNear), -zNear / (zFar - zNear)); - m[3] = vector(0.f, 0.f, 0.f, 1.f); - - return m; -} - -} -} - -#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/morton.hlsl b/include/nbl/builtin/hlsl/morton.hlsl new file mode 100644 index 0000000000..9ba33ffb3d --- /dev/null +++ b/include/nbl/builtin/hlsl/morton.hlsl @@ -0,0 +1,661 @@ +#ifndef _NBL_BUILTIN_HLSL_MORTON_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MORTON_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/concepts/core.hlsl" +#include "nbl/builtin/hlsl/bit.hlsl" +#include "nbl/builtin/hlsl/functional.hlsl" +#include "nbl/builtin/hlsl/emulated/int64_t.hlsl" +#include "nbl/builtin/hlsl/mpl.hlsl" +#include "nbl/builtin/hlsl/portable/vector_t.hlsl" + +// TODO: mega macro to get functional plus, minus, plus_assign, minus_assign + +namespace nbl +{ +namespace hlsl +{ +namespace morton +{ + +namespace impl +{ + +// Valid dimension for a morton code +template +NBL_BOOL_CONCEPT Dimension = 1 < D && D < 5; + +template && concepts::Scalar) +NBL_CONSTEXPR_FUNC bool verifyAnyBitIntegral(T val) +{ + NBL_IF_CONSTEXPR(is_signed_v) + { + // include the msb + NBL_CONSTEXPR_FUNC_SCOPE_VAR T mask = ~((uint64_t(1) << (Bits-1)) - 1); + const bool allZero = ((val & mask) == 0); + const bool allOne = ((val & mask) == mask); + return allZero || allOne; + } else + { + NBL_CONSTEXPR_FUNC_SCOPE_VAR T mask = ~((uint64_t(1) << Bits) - 1); + const bool allZero = ((val & mask) == 0); + return allZero; + } +} + +template && concepts::Scalar) +NBL_CONSTEXPR_FUNC bool verifyAnyBitIntegralVec(vector vec) +{ + array_get, T> getter; + NBL_UNROLL + for (uint16_t i = 0; i < Dim; i++) + if (!verifyAnyBitIntegral(getter(vec, i))) return false; + return true; +} + + +// --------------------------------------------------------- MORTON ENCOE/DECODE MASKS --------------------------------------------------- + +NBL_CONSTEXPR uint16_t CodingStages = 5; + +template +struct coding_mask; + +template +NBL_CONSTEXPR T coding_mask_v = _static_cast(coding_mask::value); + +// constexpr vector is not supported since it is not a fundamental type, which means it cannot be stored or leaked outside of constexpr context, it can only exist transiently. So the only way to return vector is to make the function consteval. Thus, we use macro to inline where it is used. +#define NBL_MORTON_INTERLEAVE_MASKS(STORAGE_T, DIM, BITS, NAMESPACE_PREFIX) _static_cast >(\ + truncate >(\ + vector(NAMESPACE_PREFIX coding_mask_v< DIM, BITS, 0>,\ + NAMESPACE_PREFIX coding_mask_v< DIM, BITS, 0> << 1,\ + NAMESPACE_PREFIX coding_mask_v< DIM, BITS, 0> << 2,\ + NAMESPACE_PREFIX coding_mask_v< DIM, BITS, 0> << 3))) + + +template +struct sign_mask : integral_constant {}; + +template +NBL_CONSTEXPR T sign_mask_v = _static_cast(sign_mask::value); + +#define NBL_MORTON_SIGN_MASKS(STORAGE_T, DIM, BITS) _static_cast >(\ + truncate >(\ + vector(sign_mask_v< DIM, BITS >,\ + sign_mask_v< DIM, BITS > << 1,\ + sign_mask_v< DIM, BITS > << 2,\ + sign_mask_v< DIM, BITS > << 3))) + +// 0th stage will be special: to avoid masking twice during encode/decode, and to get a proper mask that only gets the relevant bits out of a morton code, the 0th stage +// mask also considers the total number of bits we're cnsidering for a code (all other masks operate on a bit-agnostic basis). +#define NBL_HLSL_MORTON_SPECIALIZE_FIRST_CODING_MASK(DIM, BASE_VALUE) template struct coding_mask\ +{\ + enum : uint64_t { _Bits = Bits };\ + NBL_CONSTEXPR_STATIC_INLINE uint64_t KilloffMask = _Bits * DIM < 64 ? (uint64_t(1) << (_Bits * DIM)) - 1 : ~uint64_t(0);\ + NBL_CONSTEXPR_STATIC_INLINE uint64_t value = uint64_t(BASE_VALUE) & KilloffMask;\ +}; + +#define NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(DIM, STAGE, BASE_VALUE) template struct coding_mask\ +{\ + NBL_CONSTEXPR_STATIC_INLINE uint64_t value = uint64_t(BASE_VALUE);\ +}; + +// Final stage mask also counts exact number of bits, although maybe it's not necessary +#define NBL_HLSL_MORTON_SPECIALIZE_LAST_CODING_MASKS template struct coding_mask\ +{\ + enum : uint64_t { _Bits = Bits };\ + NBL_CONSTEXPR_STATIC_INLINE uint64_t value = (uint64_t(1) << _Bits) - 1;\ +}; + +NBL_HLSL_MORTON_SPECIALIZE_FIRST_CODING_MASK(2, 0x5555555555555555ull) // Groups bits by 1 on, 1 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(2, 1, 0x3333333333333333ull) // Groups bits by 2 on, 2 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(2, 2, 0x0F0F0F0F0F0F0F0Full) // Groups bits by 4 on, 4 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(2, 3, 0x00FF00FF00FF00FFull) // Groups bits by 8 on, 8 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(2, 4, 0x0000FFFF0000FFFFull) // Groups bits by 16 on, 16 off + +NBL_HLSL_MORTON_SPECIALIZE_FIRST_CODING_MASK(3, 0x9249249249249249ull) // Groups bits by 1 on, 2 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(3, 1, 0x30C30C30C30C30C3ull) // Groups bits by 2 on, 4 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(3, 2, 0xF00F00F00F00F00Full) // Groups bits by 4 on, 8 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(3, 3, 0x00FF0000FF0000FFull) // Groups bits by 8 on, 16 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(3, 4, 0xFFFF00000000FFFFull) // Groups bits by 16 on, 32 off + +NBL_HLSL_MORTON_SPECIALIZE_FIRST_CODING_MASK(4, 0x1111111111111111ull) // Groups bits by 1 on, 3 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(4, 1, 0x0303030303030303ull) // Groups bits by 2 on, 6 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(4, 2, 0x000F000F000F000Full) // Groups bits by 4 on, 12 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(4, 3, 0x000000FF000000FFull) // Groups bits by 8 on, 24 off +NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK(4, 4, 0x000000000000FFFFull) // Groups bits by 16 on, 48 off (unused but here for completion + likely keeps compiler from complaining) + +NBL_HLSL_MORTON_SPECIALIZE_LAST_CODING_MASKS + +#undef NBL_HLSL_MORTON_SPECIALIZE_LAST_CODING_MASK +#undef NBL_HLSL_MORTON_SPECIALIZE_CODING_MASK +#undef NBL_HLSL_MORTON_SPECIALIZE_FIRST_CODING_MASK + +// ----------------------------------------------------------------- MORTON TRANSCODER --------------------------------------------------- +template && Dim * Bits <= 64 && 8 * sizeof(encode_t) == mpl::max_v, uint64_t(16)>) +struct Transcoder +{ + using decode_component_t = conditional_t<(Bits > 16), uint32_t, uint16_t>; + using decode_t = vector; + + template ) + /** + * @brief Interleaves each coordinate with `Dim - 1` zeros inbetween each bit, and left-shifts each by their coordinate index + * + * @param [in] decodedValue Cartesian coordinates to interleave and shift + */ + NBL_CONSTEXPR_STATIC portable_vector_t interleaveShift(NBL_CONST_REF_ARG(T) decodedValue) + { + left_shift_operator > leftShift; + portable_vector_t interleaved = _static_cast >(decodedValue) & coding_mask_v; + + // Read this to understand how interleaving and spreading bits works https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ + #define ENCODE_LOOP_ITERATION(I) NBL_IF_CONSTEXPR(Bits > (uint16_t(1) << I))\ + {\ + interleaved = interleaved | leftShift(interleaved, (uint16_t(1) << I) * (Dim - 1));\ + interleaved = interleaved & coding_mask_v;\ + } + ENCODE_LOOP_ITERATION(4) + ENCODE_LOOP_ITERATION(3) + ENCODE_LOOP_ITERATION(2) + ENCODE_LOOP_ITERATION(1) + ENCODE_LOOP_ITERATION(0) + + #undef ENCODE_LOOP_ITERATION + + // After interleaving, shift each coordinate left by their index + return leftShift(interleaved, truncate >(vector(0, 1, 2, 3))); + } + + template + /** + * @brief Encodes a vector of cartesian coordinates as a Morton code + * + * @param [in] decodedValue Cartesian coordinates to encode + */ + NBL_CONSTEXPR_STATIC encode_t encode(NBL_CONST_REF_ARG(T) decodedValue) + { + const portable_vector_t interleaveShifted = interleaveShift(decodedValue); + + array_get, encode_t> getter; + encode_t encoded = getter(interleaveShifted, 0); + + NBL_UNROLL + for (uint16_t i = 1; i < Dim; i++) + encoded = encoded | getter(interleaveShifted, i); + + return encoded; + } + + /** + * @brief Decodes a Morton code back to a vector of cartesian coordinates + * + * @param [in] encodedValue Representation of a Morton code (binary code, not the morton class defined below) + */ + NBL_CONSTEXPR_STATIC decode_t decode(NBL_CONST_REF_ARG(encode_t) encodedValue) + { + arithmetic_right_shift_operator encodedRightShift; + portable_vector_t decoded; + array_set, encode_t> setter; + // Write initial values into decoded + NBL_UNROLL + for (uint16_t i = 0; i < Dim; i++) + setter(decoded, i, encodedRightShift(encodedValue, i)); + + arithmetic_right_shift_operator > rightShift; + + #define DECODE_LOOP_ITERATION(I) NBL_IF_CONSTEXPR(Bits > (uint16_t(1) << I))\ + {\ + decoded = decoded & coding_mask_v;\ + decoded = decoded | rightShift(decoded, (uint16_t(1) << I) * (Dim - 1));\ + } + + DECODE_LOOP_ITERATION(0) + DECODE_LOOP_ITERATION(1) + DECODE_LOOP_ITERATION(2) + DECODE_LOOP_ITERATION(3) + DECODE_LOOP_ITERATION(4) + + #undef DECODE_LOOP_ITERATION + + // If `Bits` is greater than half the bitwidth of the decode type, then we can avoid `&`ing against the last mask since duplicated MSB get truncated + NBL_IF_CONSTEXPR(Bits > 4 * sizeof(typename vector_traits::scalar_type)) + return _static_cast(decoded); + else + return _static_cast(decoded & coding_mask_v); + } +}; + +// ---------------------------------------------------- COMPARISON OPERATORS --------------------------------------------------------------- +// Here because no partial specialization of methods +// `BitsAlreadySpread` assumes both pre-interleaved and pre-shifted + +template +NBL_BOOL_CONCEPT Comparable = concepts::IntegralLikeScalar && is_signed_v == Signed && ((BitsAlreadySpread && sizeof(I) == sizeof(storage_t)) || (!BitsAlreadySpread && 8 * sizeof(I) == mpl::max_v, uint64_t(16)>)); + +template +struct Equal; + +template +struct Equal +{ + template) + NBL_CONSTEXPR_STATIC vector __call(NBL_CONST_REF_ARG(storage_t) value, NBL_CONST_REF_ARG(portable_vector_t) rhs) + { + const portable_vector_t InterleaveMasks = NBL_MORTON_INTERLEAVE_MASKS(storage_t, D, Bits, ); + const portable_vector_t zeros = promote >(_static_cast(0)); + + const portable_vector_t rhsCasted = _static_cast >(rhs); + const portable_vector_t xored = rhsCasted ^ (InterleaveMasks & value); + equal_to > _equal; + return _equal(xored, zeros); + } +}; + +template +struct Equal +{ + template) + NBL_CONSTEXPR_STATIC vector __call(NBL_CONST_REF_ARG(storage_t) value, NBL_CONST_REF_ARG(vector) rhs) + { + using right_sign_t = conditional_t, make_unsigned_t >; + using transcoder_t = Transcoder; + const portable_vector_t interleaved = _static_cast >(transcoder_t::interleaveShift(_static_cast(rhs))); + return Equal::template __call(value, interleaved); + } +}; + +template +struct BaseComparison; + +// Aux variable that has only the sign bit for the first of D dimensions +template +NBL_CONSTEXPR uint64_t SignMask = uint64_t(1) << (D * (Bits - 1)); + +template +struct BaseComparison +{ + template) + NBL_CONSTEXPR_STATIC vector __call(NBL_CONST_REF_ARG(storage_t) value, NBL_CONST_REF_ARG(portable_vector_t) rhs) + { + const portable_vector_t InterleaveMasks = NBL_MORTON_INTERLEAVE_MASKS(storage_t, D, Bits, ); + const portable_vector_t SignMasks = NBL_MORTON_SIGN_MASKS(storage_t, D, Bits); + ComparisonOp comparison; + NBL_IF_CONSTEXPR(Signed) + { + // Obtain a vector of deinterleaved coordinates and flip their sign bits + portable_vector_t thisCoord = (InterleaveMasks & value) ^ SignMasks; + // rhs already deinterleaved, just have to cast type and flip sign + const portable_vector_t rhsCoord = _static_cast >(rhs) ^ SignMasks; + + return comparison(thisCoord, rhsCoord); + } + else + { + // Obtain a vector of deinterleaved coordinates + portable_vector_t thisCoord = InterleaveMasks & value; + // rhs already deinterleaved, just have to cast type + const portable_vector_t rhsCoord = _static_cast >(rhs); + + return comparison(thisCoord, rhsCoord); + } + + } +}; + +template +struct BaseComparison +{ + template) + NBL_CONSTEXPR_STATIC vector __call(NBL_CONST_REF_ARG(storage_t) value, NBL_CONST_REF_ARG(vector) rhs) + { + using right_sign_t = conditional_t, make_unsigned_t >; + using transcoder_t = Transcoder; + const portable_vector_t interleaved = _static_cast >(transcoder_t::interleaveShift(_static_cast(rhs))); + return BaseComparison::template __call(value, interleaved); + } +}; + +template +struct LessThan : BaseComparison > > {}; + +template +struct LessEqual : BaseComparison > > {}; + +template +struct GreaterThan : BaseComparison > > {}; + +template +struct GreaterEqual : BaseComparison > > {}; + +} //namespace impl + +// Making this even slightly less ugly is blocked by https://github.com/microsoft/DirectXShaderCompiler/issues/7006 +// In particular, `Masks` should be a `const static` member field instead of appearing in every method using it +template && D * Bits <= 64) +struct code +{ + using this_t = code; + using this_signed_t = code; + NBL_CONSTEXPR_STATIC uint16_t TotalBitWidth = D * Bits; + using storage_t = conditional_t<(TotalBitWidth > 16), conditional_t<(TotalBitWidth > 32), _uint64_t, uint32_t>, uint16_t>; + + using transcoder_t = impl::Transcoder; + using decode_component_t = conditional_t, + typename transcoder_t::decode_component_t>; + + storage_t value; + + // ---------------------------------------------------- CONSTRUCTORS --------------------------------------------------------------- + + #ifndef __HLSL_VERSION + + code() = default; + + #endif + + /** + * @brief Creates a Morton code from a set of integral cartesian coordinates + * + * @param [in] cartesian Coordinates to encode. Signedness MUST match the signedness of this Morton code class + */ + template + NBL_CONSTEXPR_STATIC enable_if_t , this_t> + create(NBL_CONST_REF_ARG(vector) cartesian) + { + this_t retVal; + assert((impl::verifyAnyBitIntegralVec(cartesian))); + using decode_t = typename transcoder_t::decode_t; + retVal.value = transcoder_t::encode(_static_cast(cartesian)); + return retVal; + } + + // CPP can also have an actual constructor + #ifndef __HLSL_VERSION + + /** + * @brief Creates a Morton code from a set of cartesian coordinates + * + * @param [in] cartesian Coordinates to encode + */ + template + inline explicit code(NBL_CONST_REF_ARG(vector) cartesian) + { + *this = create(cartesian); + } + + /** + * @brief Decodes this Morton code back to a set of cartesian coordinates + */ + template == Signed) + constexpr explicit operator vector() const noexcept; + + #endif + + // ------------------------------------------------------- BITWISE OPERATORS ------------------------------------------------- + + NBL_CONSTEXPR_FUNC this_t operator&(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC + { + this_t retVal; + retVal.value = value & rhs.value; + return retVal; + } + + NBL_CONSTEXPR_FUNC this_t operator|(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC + { + this_t retVal; + retVal.value = value | rhs.value; + return retVal; + } + + NBL_CONSTEXPR_FUNC this_t operator^(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC + { + this_t retVal; + retVal.value = value ^ rhs.value; + return retVal; + } + + NBL_CONSTEXPR_FUNC this_t operator~() NBL_CONST_MEMBER_FUNC + { + this_t retVal; + retVal.value = ~value; + return retVal; + } + + // Only valid in CPP + #ifndef __HLSL_VERSION + + constexpr this_t operator<<(uint16_t bits) const; + + constexpr this_t operator>>(uint16_t bits) const; + + #endif + + // ------------------------------------------------------- UNARY ARITHMETIC OPERATORS ------------------------------------------------- + + NBL_CONSTEXPR_FUNC this_signed_t operator-() NBL_CONST_MEMBER_FUNC + { + this_t zero; + zero.value = _static_cast(0); + #ifndef __HLSL_VERSION + return zero - *this; + #else + return zero - this; + #endif + } + + // ------------------------------------------------------- BINARY ARITHMETIC OPERATORS ------------------------------------------------- + + // put 1 bits everywhere in the bits the current axis is not using + // then extract just the axis bits for the right hand coordinate + // carry-1 will propagate the bits across the already set bits + // then clear out the bits not belonging to current axis + // Note: Its possible to clear on `this` and fill on `rhs` but that will + // disable optimizations, we expect the compiler to optimize a lot if the + // value of `rhs` is known at compile time, e.g. `static_cast>(glm::ivec3(1,0,0))` + NBL_CONSTEXPR_FUNC this_t operator+(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC + { + const portable_vector_t InterleaveMasks = NBL_MORTON_INTERLEAVE_MASKS(storage_t, D, Bits, impl::); + bit_not > bitnot; + // For each coordinate, leave its bits intact and turn every other bit ON + const portable_vector_t counterMaskedValue = bitnot(InterleaveMasks) | value; + // For each coordinate in rhs, leave its bits intact and turn every other bit OFF + const portable_vector_t maskedRhsValue = InterleaveMasks & rhs.value; + // Add these coordinate-wise, then turn all bits not belonging to the current coordinate OFF + const portable_vector_t interleaveShiftedResult = (counterMaskedValue + maskedRhsValue) & InterleaveMasks; + // Re-encode the result + array_get, storage_t> getter; + this_t retVal; + retVal.value = getter(interleaveShiftedResult, 0); + NBL_UNROLL + for (uint16_t i = 1; i < D; i++) + retVal.value = retVal.value | getter(interleaveShiftedResult, i); + return retVal; + } + + // This is the dual trick of the one used for addition: set all other bits to 0 so borrows propagate + NBL_CONSTEXPR_FUNC this_t operator-(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC + { + const portable_vector_t InterleaveMasks = NBL_MORTON_INTERLEAVE_MASKS(storage_t, D, Bits, impl::); + // For each coordinate, leave its bits intact and turn every other bit OFF + const portable_vector_t maskedValue = InterleaveMasks & value; + // Do the same for each coordinate in rhs + const portable_vector_t maskedRhsValue = InterleaveMasks & rhs.value; + // Subtract these coordinate-wise, then turn all bits not belonging to the current coordinate OFF + const portable_vector_t interleaveShiftedResult = (maskedValue - maskedRhsValue) & InterleaveMasks; + // Re-encode the result + array_get, storage_t> getter; + this_t retVal; + retVal.value = getter(interleaveShiftedResult, 0); + NBL_UNROLL + for (uint16_t i = 1; i < D; i++) + retVal.value = retVal.value | getter(interleaveShiftedResult, i); + + return retVal; + } + + // ------------------------------------------------------- COMPARISON OPERATORS ------------------------------------------------- + + NBL_CONSTEXPR_FUNC bool operator==(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC + { + return value == rhs.value; + } + + template) + NBL_CONSTEXPR_FUNC vector equal(NBL_CONST_REF_ARG(vector) rhs) NBL_CONST_MEMBER_FUNC + { + return impl::Equal::template __call(value, rhs); + } + + NBL_CONSTEXPR_FUNC bool operator!=(NBL_CONST_REF_ARG(this_t) rhs) NBL_CONST_MEMBER_FUNC + { + return value != rhs.value; + } + + template) + NBL_CONSTEXPR_FUNC vector notEqual(NBL_CONST_REF_ARG(vector) rhs) NBL_CONST_MEMBER_FUNC + { + return !equal(rhs); + } + + template) + NBL_CONSTEXPR_FUNC vector lessThan(NBL_CONST_REF_ARG(vector) rhs) NBL_CONST_MEMBER_FUNC + { + return impl::LessThan::template __call(value, rhs); + } + + template) + NBL_CONSTEXPR_FUNC vector lessThanEqual(NBL_CONST_REF_ARG(vector) rhs) NBL_CONST_MEMBER_FUNC + { + return impl::LessEqual::template __call(value, rhs); + } + + template) + NBL_CONSTEXPR_FUNC vector greaterThan(NBL_CONST_REF_ARG(vector) rhs) NBL_CONST_MEMBER_FUNC + { + return impl::GreaterThan::template __call(value, rhs); + } + + template) + NBL_CONSTEXPR_FUNC vector greaterThanEqual(NBL_CONST_REF_ARG(vector) rhs) NBL_CONST_MEMBER_FUNC + { + return impl::GreaterEqual::template __call(value, rhs); + } + +}; + +} //namespace morton + +// Specialize the `static_cast_helper` +namespace impl +{ + +// I must be of same signedness as the morton code, and be wide enough to hold each component +template NBL_PARTIAL_REQ_TOP(concepts::IntegralScalar) +struct static_cast_helper, morton::code, Bits, D, _uint64_t> NBL_PARTIAL_REQ_BOT(concepts::IntegralScalar) > +{ + NBL_CONSTEXPR_STATIC vector cast(NBL_CONST_REF_ARG(morton::code, Bits, D, _uint64_t>) val) + { + using storage_t = typename morton::code, Bits, D, _uint64_t>::storage_t; + return morton::impl::Transcoder::decode(val.value); + } +}; + +} // namespace impl + +template +struct left_shift_operator > +{ + using type_t = morton::code; + using storage_t = typename type_t::storage_t; + + NBL_CONSTEXPR_FUNC type_t operator()(NBL_CONST_REF_ARG(type_t) operand, uint16_t bits) + { + left_shift_operator valueLeftShift; + type_t retVal; + // Shift every coordinate by `bits` + retVal.value = valueLeftShift(operand.value, bits * D); + // Previous shift might move bits to positions that storage has available but the morton code does not use + // Un-decoding the resulting morton is still fine and produces expected results, but some operations such as equality expect these unused bits to be 0 so we mask them off + const uint64_t UsedBitsMask = Bits * D < 64 ? (uint64_t(1) << (Bits * D)) - 1 : ~uint64_t(0); + retVal.value = retVal.value & _static_cast(UsedBitsMask); + return retVal; + } +}; + +template +struct arithmetic_right_shift_operator > +{ + using type_t = morton::code; + using storage_t = typename type_t::storage_t; + + NBL_CONSTEXPR_FUNC type_t operator()(NBL_CONST_REF_ARG(type_t) operand, uint16_t bits) + { + arithmetic_right_shift_operator valueArithmeticRightShift; + type_t retVal; + // Shift every coordinate by `bits` + retVal.value = valueArithmeticRightShift(operand.value, bits * D); + return retVal; + } +}; + +// This one's uglier - have to unpack to get the expected behaviour +template +struct arithmetic_right_shift_operator > +{ + using type_t = morton::code; + using scalar_t = conditional_t<(Bits > 16), int32_t, int16_t>; + + NBL_CONSTEXPR_FUNC type_t operator()(NBL_CONST_REF_ARG(type_t) operand, uint16_t bits) + { + vector cartesian = _static_cast >(operand); + // To avoid branching, we left-shift each coordinate to put the MSB (of the encoded Morton) at the position of the MSB (of the `scalar_t` used for the decoded coordinate), + // then right-shift again to get correct sign on each coordinate + // The number of bits we shift by to put MSB of Morton at MSB of `scalar_t` is the difference between the bitwidth of `scalar_t` and Bits + const scalar_t ShiftFactor = scalar_t(8 * sizeof(scalar_t) - Bits); + cartesian <<= ShiftFactor; + cartesian >>= ShiftFactor + scalar_t(bits); + return type_t::create(cartesian); + } +}; + +#ifndef __HLSL_VERSION + +template&& D* Bits <= 64) +constexpr morton::code morton::code::operator<<(uint16_t bits) const +{ + left_shift_operator> leftShift; + return leftShift(*this, bits); +} + +template&& D* Bits <= 64) +constexpr morton::code morton::code::operator>>(uint16_t bits) const +{ + arithmetic_right_shift_operator> rightShift; + return rightShift(*this, bits); +} + +template && D* Bits <= 64) +template == Signed) +constexpr morton::code::operator vector() const noexcept +{ + return _static_cast, morton::code>(*this); +} + +#endif + +#undef NBL_MORTON_INTERLEAVE_MASKS +#undef NBL_MORTON_SIGN_MASKS + +} //namespace hlsl +} //namespace nbl + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/mpl.hlsl b/include/nbl/builtin/hlsl/mpl.hlsl index 8fb13db872..7734dea15f 100644 --- a/include/nbl/builtin/hlsl/mpl.hlsl +++ b/include/nbl/builtin/hlsl/mpl.hlsl @@ -41,7 +41,12 @@ struct countl_zero : impl::countl_zero static_assert(is_integral::value, "countl_zero type parameter must be an integral type"); }; template -NBL_CONSTEXPR T countl_zero_v = countl_zero::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR T countl_zero_v = countl_zero::value; + +template +struct is_pot : bool_constant< (N > 0 && !(N & (N - 1))) > {}; +template +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_pot_v = is_pot::value; template struct log2 @@ -49,7 +54,12 @@ struct log2 NBL_CONSTEXPR_STATIC_INLINE uint16_t value = X ? (1ull<<6)-countl_zero::value-1 : -1ull; }; template -NBL_CONSTEXPR uint64_t log2_v = log2::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint16_t log2_v = log2::value; + +template +struct log2_ceil : integral_constant + uint16_t(!is_pot_v)> {}; +template +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint16_t log2_ceil_v = log2_ceil::value; template struct rotl @@ -59,7 +69,7 @@ struct rotl NBL_CONSTEXPR_STATIC_INLINE T value = (S >= 0) ? ((X << r) | (X >> (N - r))) : (X >> (-r)) | (X << (N - (-r))); }; template -NBL_CONSTEXPR T rotl_v = rotl::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR T rotl_v = rotl::value; template struct rotr @@ -69,7 +79,7 @@ struct rotr NBL_CONSTEXPR_STATIC_INLINE T value = (S >= 0) ? ((X >> r) | (X << (N - r))) : (X << (-r)) | (X >> (N - (-r))); }; template -NBL_CONSTEXPR T rotr_v = rotr::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR T rotr_v = rotr::value; template struct align_up @@ -77,12 +87,7 @@ struct align_up NBL_CONSTEXPR_STATIC_INLINE uint64_t value = X ? (((X-1)/M+1)*M):0; }; template -NBL_CONSTEXPR uint64_t align_up_v = align_up::value; - -template -struct is_pot : bool_constant< (N > 0 && !(N & (N - 1))) > {}; -template -NBL_CONSTEXPR bool is_pot_v = is_pot::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint64_t align_up_v = align_up::value; template struct max @@ -90,7 +95,7 @@ struct max NBL_CONSTEXPR_STATIC_INLINE T value = X -NBL_CONSTEXPR T max_v = max::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR T max_v = max::value; template struct min @@ -98,7 +103,18 @@ struct min NBL_CONSTEXPR_STATIC_INLINE T value = X -NBL_CONSTEXPR T min_v = min::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR T min_v = min::value; + +template +struct round_up_to_pot : integral_constant > {}; +template +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint64_t round_up_to_pot_v = round_up_to_pot::value; + +// TODO: should rename log2 to log2_floor +template +struct round_down_to_pot : integral_constant > {}; +template +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint64_t round_down_to_pot_v = round_down_to_pot::value; template struct find_lsb @@ -106,7 +122,7 @@ struct find_lsb NBL_CONSTEXPR_STATIC_INLINE uint16_t value = log2::value; }; template -NBL_CONSTEXPR uint64_t find_lsb_v = find_lsb::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint64_t find_lsb_v = find_lsb::value; } } } diff --git a/include/nbl/builtin/hlsl/numbers.hlsl b/include/nbl/builtin/hlsl/numbers.hlsl index 6671a44756..4594596590 100644 --- a/include/nbl/builtin/hlsl/numbers.hlsl +++ b/include/nbl/builtin/hlsl/numbers.hlsl @@ -11,33 +11,33 @@ namespace numbers { template -NBL_CONSTEXPR float_t e = float_t(2.718281828459045); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t e = float_t(2.718281828459045); template -NBL_CONSTEXPR float_t log2e = float_t(1.4426950408889634); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t log2e = float_t(1.4426950408889634); template -NBL_CONSTEXPR float_t log10e = float_t(0.4342944819032518); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t log10e = float_t(0.4342944819032518); template -NBL_CONSTEXPR float_t pi = float_t(3.141592653589793); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t pi = float_t(3.141592653589793); template -NBL_CONSTEXPR float_t inv_pi = float_t(0.3183098861837907); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t inv_pi = float_t(0.3183098861837907); template -NBL_CONSTEXPR float_t inv_sqrtpi = float_t(0.5641895835477563); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t inv_sqrtpi = float_t(0.5641895835477563); template -NBL_CONSTEXPR float_t ln2 = float_t(0.6931471805599453); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t ln2 = float_t(0.6931471805599453); template -NBL_CONSTEXPR float_t inv_ln2 = float_t(1.44269504088896); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t inv_ln2 = float_t(1.44269504088896); template -NBL_CONSTEXPR float_t ln10 = float_t(2.302585092994046); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t ln10 = float_t(2.302585092994046); template -NBL_CONSTEXPR float_t sqrt2 = float_t(1.4142135623730951); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t sqrt2 = float_t(1.4142135623730951); template -NBL_CONSTEXPR float_t sqrt3 = float_t(1.7320508075688772); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t sqrt3 = float_t(1.7320508075688772); template -NBL_CONSTEXPR float_t inv_sqrt3 = float_t(0.5773502691896257); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t inv_sqrt3 = float_t(0.5773502691896257); template -NBL_CONSTEXPR float_t egamma = float_t(0.5772156649015329); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t egamma = float_t(0.5772156649015329); template -NBL_CONSTEXPR float_t phi = float_t(1.618033988749895); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float_t phi = float_t(1.618033988749895); } } diff --git a/include/nbl/builtin/hlsl/portable/int64_t.hlsl b/include/nbl/builtin/hlsl/portable/int64_t.hlsl new file mode 100644 index 0000000000..2dffa40a2d --- /dev/null +++ b/include/nbl/builtin/hlsl/portable/int64_t.hlsl @@ -0,0 +1,36 @@ +#ifndef _NBL_BUILTIN_HLSL_PORTABLE_INT64_T_INCLUDED_ +#define _NBL_BUILTIN_HLSL_PORTABLE_INT64_T_INCLUDED_ + +#include +#include + +// define NBL_FORCE_EMULATED_INT_64 to force using emulated int64 types + +namespace nbl +{ +namespace hlsl +{ +#ifdef __HLSL_VERSION +#ifdef NBL_FORCE_EMULATED_INT_64 +template +using portable_uint64_t = emulated_uint64_t; +template +using portable_int64_t = emulated_int64_t; +#else +template +using portable_uint64_t = typename conditional::shaderInt64, uint64_t, emulated_uint64_t>::type; +template +using portable_int64_t = typename conditional::shaderInt64, int64_t, emulated_int64_t>::type; +#endif + +#else +template +using portable_uint64_t = uint64_t; +template +using portable_int64_t = int64_t; +#endif + +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/portable/vector_t.hlsl b/include/nbl/builtin/hlsl/portable/vector_t.hlsl index ace199e20b..16d5b40f81 100644 --- a/include/nbl/builtin/hlsl/portable/vector_t.hlsl +++ b/include/nbl/builtin/hlsl/portable/vector_t.hlsl @@ -3,6 +3,7 @@ #include #include +#include namespace nbl { @@ -36,19 +37,53 @@ template using portable_vector_t4 = portable_vector_t; #ifdef __HLSL_VERSION +// Float template using portable_float64_t2 = portable_vector_t2 >; template using portable_float64_t3 = portable_vector_t3 >; template using portable_float64_t4 = portable_vector_t4 >; + +// Uint +template +using portable_uint64_t2 = portable_vector_t2 >; +template +using portable_uint64_t3 = portable_vector_t3 >; +template +using portable_uint64_t4 = portable_vector_t4 >; + +//Int +template +using portable_int64_t2 = portable_vector_t2 >; +template +using portable_int64_t3 = portable_vector_t3 >; +template +using portable_int64_t4 = portable_vector_t4 >; #else +// Float template using portable_float64_t2 = portable_vector_t2; template using portable_float64_t3 = portable_vector_t3; template using portable_float64_t4 = portable_vector_t4; + +// Uint +template +using portable_uint64_t2 = portable_vector_t2; +template +using portable_uint64_t3 = portable_vector_t3; +template +using portable_uint64_t4 = portable_vector_t4; + +// Int +template +using portable_int64_t2 = portable_vector_t2; +template +using portable_int64_t3 = portable_vector_t3; +template +using portable_int64_t4 = portable_vector_t4; #endif } diff --git a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl index 593e267a26..9413bcee98 100644 --- a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl @@ -69,12 +69,9 @@ struct CascadeAccumulator // most of this code is stolen from https://cg.ivd.kit.edu/publications/2018/rwmc/tool/split.cpp void addSample(uint32_t sampleCount, input_sample_type _sample) { - const float32_t2 unpackedParams = hlsl::unpackHalf2x16(splattingParameters.packedLog2); - const cascade_layer_scalar_type log2Start = unpackedParams[0]; - const cascade_layer_scalar_type log2Base = unpackedParams[1]; const cascade_layer_scalar_type luma = getLuma(_sample); const cascade_layer_scalar_type log2Luma = log2(luma); - const cascade_layer_scalar_type cascade = log2Luma * 1.f / log2Base - log2Start / log2Base; + const cascade_layer_scalar_type cascade = log2Luma * splattingParameters.rcpLog2Base - splattingParameters.baseRootOfStart; const cascade_layer_scalar_type clampedCascade = clamp(cascade, 0, CascadeCount - 1); // c<=0 -> 0, c>=Count-1 -> Count-1 uint32_t lowerCascadeIndex = floor(cascade); @@ -85,7 +82,7 @@ struct CascadeAccumulator // handle super bright sample case if (cascade > CascadeCount - 1) - lowerCascadeWeight = exp2(log2Start + log2Base * (CascadeCount - 1) - log2Luma); + lowerCascadeWeight = splattingParameters.lastCascadeLuma / luma; accumulation.addSampleIntoCascadeEntry(_sample, lowerCascadeIndex, lowerCascadeWeight, higherCascadeWeight, sampleCount); } diff --git a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl index 7509eac493..6ef687c506 100644 --- a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl @@ -21,7 +21,7 @@ struct ResolveParameters float NOverKappa; }; -ResolveParameters computeResolveParameters(float base, uint32_t sampleCount, float minReliableLuma, float kappa, uint32_t cascadeSize) +inline ResolveParameters computeResolveParameters(float base, uint32_t sampleCount, float minReliableLuma, float kappa, uint32_t cascadeSize) { ResolveParameters retval; retval.lastCascadeIndex = cascadeSize - 1u; diff --git a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl index c549d83be6..a3a3520415 100644 --- a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl @@ -2,6 +2,7 @@ #define _NBL_BUILTIN_HLSL_RWMC_SPLATTING_PARAMETERS_HLSL_INCLUDED_ #include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" namespace nbl { @@ -12,10 +13,22 @@ namespace rwmc struct SplattingParameters { - // float16_t log2Start; 0 - // float16_t log2Base; 1 - // pack as Half2x16 - int32_t packedLog2; + using scalar_t = float; + + static SplattingParameters create(const scalar_t base, const scalar_t start, const uint32_t cascadeCount) + { + SplattingParameters retval; + const scalar_t log2Base = hlsl::log2(base); + const scalar_t log2Start = hlsl::log2(start); + retval.lastCascadeLuma = hlsl::exp2(log2Start + log2Base * (cascadeCount - 1)); + retval.rcpLog2Base = scalar_t(1.0) / log2Base; + retval.baseRootOfStart = log2Start * retval.rcpLog2Base; + return retval; + } + + scalar_t lastCascadeLuma; + scalar_t baseRootOfStart; + scalar_t rcpLog2Base; }; } diff --git a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl index d8f777d277..906cad512b 100644 --- a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl +++ b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl @@ -41,34 +41,6 @@ NBL_CONCEPT_END( template NBL_BOOL_CONCEPT ResolveAccessor = ResolveAccessorBase && concepts::accessors::LoadableImage; -template -struct ResolveAccessorAdaptor -{ - using output_scalar_type = OutputScalar; - using output_type = vector; - NBL_CONSTEXPR int32_t image_dimension = 2; - - RWTexture2DArray cascade; - - float32_t calcLuma(NBL_REF_ARG(float32_t3) col) - { - return hlsl::dot(colorspace::scRGB::ToXYZ()[1], col); - } - - template - output_type get(vector uv, uint16_t layer) - { - uint32_t imgWidth, imgHeight, layers; - cascade.GetDimensions(imgWidth, imgHeight, layers); - int16_t2 cascadeImageDimension = int16_t2(imgWidth, imgHeight); - - if (any(uv < int16_t2(0, 0)) || any(uv > cascadeImageDimension)) - return vector(0, 0, 0, 0); - - return cascade.Load(int32_t3(uv, int32_t(layer))); - } -}; - template && ResolveAccessor) struct Resolver { diff --git a/include/nbl/builtin/hlsl/sampling/basic.hlsl b/include/nbl/builtin/hlsl/sampling/basic.hlsl index d0738dd930..9c575a22ce 100644 --- a/include/nbl/builtin/hlsl/sampling/basic.hlsl +++ b/include/nbl/builtin/hlsl/sampling/basic.hlsl @@ -19,14 +19,14 @@ template) struct PartitionRandVariable { using floating_point_type = T; - using uint_type = typename unsigned_integer_of_size::type; + using uint_type = unsigned_integer_of_size_t; - bool operator()(floating_point_type leftProb, NBL_REF_ARG(floating_point_type) xi, NBL_REF_ARG(floating_point_type) rcpChoiceProb) + bool operator()(NBL_REF_ARG(floating_point_type) xi, NBL_REF_ARG(floating_point_type) rcpChoiceProb) { - const floating_point_type NEXT_ULP_AFTER_UNITY = bit_cast(bit_cast(floating_point_type(1.0)) + uint_type(1u)); - const bool pickRight = xi >= leftProb * NEXT_ULP_AFTER_UNITY; + const floating_point_type NextULPAfterUnity = bit_cast(bit_cast(floating_point_type(1.0)) + uint_type(1u)); + const bool pickRight = xi >= leftProb * NextULPAfterUnity; - // This is all 100% correct taking into account the above NEXT_ULP_AFTER_UNITY + // This is all 100% correct taking into account the above NextULPAfterUnity xi -= pickRight ? leftProb : floating_point_type(0.0); rcpChoiceProb = floating_point_type(1.0) / (pickRight ? (floating_point_type(1.0) - leftProb) : leftProb); @@ -34,6 +34,8 @@ struct PartitionRandVariable return pickRight; } + + floating_point_type leftProb; }; diff --git a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl index 746713e4c4..a74869990f 100644 --- a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl +++ b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl @@ -24,7 +24,7 @@ struct Bilinear using vector3_type = vector; using vector4_type = vector; - static Bilinear create(NBL_CONST_REF_ARG(vector4_type) bilinearCoeffs) + static Bilinear create(const vector4_type bilinearCoeffs) { Bilinear retval; retval.bilinearCoeffs = bilinearCoeffs; @@ -32,22 +32,22 @@ struct Bilinear return retval; } - vector2_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) _u) + vector2_type generate(NBL_REF_ARG(scalar_type) rcpPdf, const vector2_type _u) { - vector2_type u = _u; + vector2_type u; Linear lineary = Linear::create(twiceAreasUnderXCurve); - u.y = lineary.generate(u.y); + u.y = lineary.generate(_u.y); const vector2_type ySliceEndPoints = vector2_type(nbl::hlsl::mix(bilinearCoeffs[0], bilinearCoeffs[2], u.y), nbl::hlsl::mix(bilinearCoeffs[1], bilinearCoeffs[3], u.y)); Linear linearx = Linear::create(ySliceEndPoints); - u.x = linearx.generate(u.x); + u.x = linearx.generate(_u.x); rcpPdf = (twiceAreasUnderXCurve[0] + twiceAreasUnderXCurve[1]) / (4.0 * nbl::hlsl::mix(ySliceEndPoints[0], ySliceEndPoints[1], u.x)); return u; } - scalar_type pdf(NBL_CONST_REF_ARG(vector2_type) u) + scalar_type pdf(const vector2_type u) { return 4.0 * nbl::hlsl::mix(nbl::hlsl::mix(bilinearCoeffs[0], bilinearCoeffs[1], u.x), nbl::hlsl::mix(bilinearCoeffs[2], bilinearCoeffs[3], u.x), u.y) / (bilinearCoeffs[0] + bilinearCoeffs[1] + bilinearCoeffs[2] + bilinearCoeffs[3]); } diff --git a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl index 93cea06ee0..9474642f4c 100644 --- a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl +++ b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl @@ -21,7 +21,7 @@ struct BoxMullerTransform using scalar_type = T; using vector2_type = vector; - vector2_type operator()(vector2_type xi) + vector2_type operator()(const vector2_type xi) { scalar_type sinPhi, cosPhi; math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); diff --git a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl index 1a5c96b6df..841fc9ff2d 100644 --- a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl +++ b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl @@ -17,7 +17,7 @@ namespace sampling { template -vector concentricMapping(vector _u) +vector concentricMapping(const vector _u) { //map [0;1]^2 to [-1;1]^2 vector u = 2.0f * _u - hlsl::promote >(1.0); diff --git a/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl b/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl index 9f95bf2ee5..ddbb961300 100644 --- a/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl +++ b/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl @@ -22,26 +22,26 @@ struct ProjectedHemisphere using vector_t2 = vector; using vector_t3 = vector; - static vector_t3 generate(vector_t2 _sample) + static vector_t3 generate(const vector_t2 _sample) { vector_t2 p = concentricMapping(_sample * T(0.99999) + T(0.000005)); T z = hlsl::sqrt(hlsl::max(T(0.0), T(1.0) - p.x * p.x - p.y * p.y)); return vector_t3(p.x, p.y, z); } - static T pdf(T L_z) + static T pdf(const T L_z) { return L_z * numbers::inv_pi; } template > - static sampling::quotient_and_pdf quotient_and_pdf(T L) + static sampling::quotient_and_pdf quotient_and_pdf(const T L) { return sampling::quotient_and_pdf::create(hlsl::promote(1.0), pdf(L)); } template > - static sampling::quotient_and_pdf quotient_and_pdf(vector_t3 L) + static sampling::quotient_and_pdf quotient_and_pdf(const vector_t3 L) { return sampling::quotient_and_pdf::create(hlsl::promote(1.0), pdf(L.z)); } @@ -77,7 +77,7 @@ struct ProjectedSphere } template > - static sampling::quotient_and_pdf quotient_and_pdf(vector_t3 L) + static sampling::quotient_and_pdf quotient_and_pdf(const vector_t3 L) { return sampling::quotient_and_pdf::create(hlsl::promote(1.0), pdf(L.z)); } diff --git a/include/nbl/builtin/hlsl/sampling/linear.hlsl b/include/nbl/builtin/hlsl/sampling/linear.hlsl index ddd7bcf8df..6c3cf1fad9 100644 --- a/include/nbl/builtin/hlsl/sampling/linear.hlsl +++ b/include/nbl/builtin/hlsl/sampling/linear.hlsl @@ -21,7 +21,7 @@ struct Linear using scalar_type = T; using vector2_type = vector; - static Linear create(NBL_CONST_REF_ARG(vector2_type) linearCoeffs) // start and end importance values (start, end) + static Linear create(const vector2_type linearCoeffs) // start and end importance values (start, end) { Linear retval; retval.linearCoeffStart = linearCoeffs[0]; @@ -32,7 +32,7 @@ struct Linear return retval; } - scalar_type generate(scalar_type u) + scalar_type generate(const scalar_type u) { return hlsl::mix(u, (linearCoeffStart - hlsl::sqrt(squaredCoeffStart + u * squaredCoeffDiff)) * rcpDiff, hlsl::abs(rcpDiff) < numeric_limits::max); } diff --git a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl index f2f29ed12b..e60fe28423 100644 --- a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl @@ -33,23 +33,23 @@ struct ProjectedSphericalTriangle return retval; } - vector4_type computeBilinearPatch(NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF) + vector4_type computeBilinearPatch(const vector3_type receiverNormal, bool isBSDF) { const scalar_type minimumProjSolidAngle = 0.0; matrix m = matrix(tri.vertex0, tri.vertex1, tri.vertex2); - const vector3_type bxdfPdfAtVertex = math::conditionalAbsOrMax(isBSDF, nbl::hlsl::mul(m, receiverNormal), (vector3_type)minimumProjSolidAngle); + const vector3_type bxdfPdfAtVertex = math::conditionalAbsOrMax(isBSDF, nbl::hlsl::mul(m, receiverNormal), hlsl::promote(minimumProjSolidAngle)); return bxdfPdfAtVertex.yyxz; } - vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REF_ARG(vector2_type) _u) + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, scalar_type solidAngle, const vector3_type cos_vertices, const vector3_type sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, const vector3_type receiverNormal, bool isBSDF, const vector2_type _u) { vector2_type u; // pre-warp according to proj solid angle approximation vector4_type patch = computeBilinearPatch(receiverNormal, isBSDF); Bilinear bilinear = Bilinear::create(patch); - u = bilinear.generate(rcpPdf, u); + u = bilinear.generate(rcpPdf, _u); // now warp the points onto a spherical triangle const vector3_type L = sphtri.generate(solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); @@ -58,7 +58,7 @@ struct ProjectedSphericalTriangle return L; } - vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REF_ARG(vector2_type) u) + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, const vector3_type receiverNormal, bool isBSDF, const vector2_type u) { scalar_type cos_a, cos_c, csc_b, csc_c; vector3_type cos_vertices, sin_vertices; @@ -66,7 +66,7 @@ struct ProjectedSphericalTriangle return generate(rcpPdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, receiverNormal, isBSDF, u); } - scalar_type pdf(scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REF_ARG(vector3_type) L) + scalar_type pdf(scalar_type solidAngle, const vector3_type cos_vertices, const vector3_type sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, const vector3_type receiverNormal, bool receiverWasBSDF, const vector3_type L) { scalar_type pdf; const vector2_type u = sphtri.generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); @@ -76,7 +76,7 @@ struct ProjectedSphericalTriangle return pdf * bilinear.pdf(u); } - scalar_type pdf(NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REF_ARG(vector3_type) L) + scalar_type pdf(const vector3_type receiverNormal, bool receiverWasBSDF, const vector3_type L) { scalar_type pdf; const vector2_type u = sphtri.generateInverse(pdf, L); diff --git a/include/nbl/builtin/hlsl/sampling/quantized_sequence.hlsl b/include/nbl/builtin/hlsl/sampling/quantized_sequence.hlsl new file mode 100644 index 0000000000..64573ac85f --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/quantized_sequence.hlsl @@ -0,0 +1,416 @@ +// 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_BUILTIN_HLSL_SAMPLING_QUANTIZED_SEQUENCE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_QUANTIZED_SEQUENCE_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/bit.hlsl" +#include "nbl/builtin/hlsl/glsl_compat/core.hlsl" +#include "nbl/builtin/hlsl/concepts/vector.hlsl" +#include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct QuantizedSequence; + + +namespace impl +{ +template +struct unorm_constant; +template<> +struct unorm_constant { NBL_CONSTEXPR_STATIC_INLINE uint32_t value = 0x3d888889u; }; +template<> +struct unorm_constant { NBL_CONSTEXPR_STATIC_INLINE uint32_t value = 0x3d042108u; }; +template<> +struct unorm_constant { NBL_CONSTEXPR_STATIC_INLINE uint32_t value = 0x3b808081u; }; +template<> +struct unorm_constant { NBL_CONSTEXPR_STATIC_INLINE uint32_t value = 0x3a802008u; }; +template<> +struct unorm_constant { NBL_CONSTEXPR_STATIC_INLINE uint32_t value = 0x37800080u; }; +template<> +struct unorm_constant { NBL_CONSTEXPR_STATIC_INLINE uint32_t value = 0x35000004u; }; +template<> +struct unorm_constant { NBL_CONSTEXPR_STATIC_INLINE uint32_t value = 0x2f800004u; }; + +// FullWidth if intend to decode before scramble, not if decode after scramble +template +struct encode_helper +{ + NBL_CONSTEXPR_STATIC_INLINE uint16_t Dim = Q::Dimension; + using sequence_type = Q; + using input_type = vector; + using uniform_storage_scalar_type = unsigned_integer_of_size_t; + using uniform_storage_type = vector; // type that holds uint bit representation of a unorm that can have 1s in MSB (normalized w.r.t whole scalar) + NBL_CONSTEXPR_STATIC_INLINE uint16_t Bits = FullWidth ? (8u * size_of_v - 1u) : sequence_type::BitsPerComponent; + NBL_CONSTEXPR_STATIC_INLINE uint32_t UNormMultiplier = (1u << Bits) - 1u; + + static sequence_type __call(const input_type unormvec) + { + uniform_storage_type asuint; + NBL_UNROLL for(uint16_t i = 0; i < Dim; i++) + asuint[i] = uniform_storage_scalar_type(unormvec[i] * UNormMultiplier); + NBL_IF_CONSTEXPR(Dim==1) + return sequence_type::create(asuint[0]); + else + return sequence_type::create(asuint); + } +}; + +template +struct decode_before_scramble_helper +{ + using storage_scalar_type = typename Q::scalar_type; + NBL_CONSTEXPR_STATIC_INLINE uint16_t Dim = Q::Dimension; + using uvec_type = vector; + using sequence_type = Q; + using return_type = vector; + NBL_CONSTEXPR_STATIC_INLINE uint32_t UNormConstant = unorm_constant::value; + + return_type operator()(const uvec_type scrambleKey) + { + uvec_type seqVal; + NBL_UNROLL for(uint16_t i = 0; i < Dim; i++) + seqVal[i] = val.get(i); + seqVal ^= scrambleKey; + return return_type(seqVal) * bit_cast >(UNormConstant); + } + + sequence_type val; +}; +template +struct decode_after_scramble_helper +{ + using storage_scalar_type = typename Q::scalar_type; + NBL_CONSTEXPR_STATIC_INLINE uint16_t Dim = Q::Dimension; + using uvec_type = vector; + using sequence_type = Q; + using return_type = vector; + NBL_CONSTEXPR_STATIC_INLINE uint32_t UNormConstant = unorm_constant::value; + + return_type operator()(NBL_CONST_REF_ARG(sequence_type) scrambleKey) + { + sequence_type scramble; + scramble.data = val.data ^ scrambleKey.data; + + uvec_type seqVal; + NBL_UNROLL for(uint16_t i = 0; i < Dim; i++) + seqVal[i] = scramble.get(i); + return return_type(seqVal) * bit_cast >(UNormConstant); + } + + sequence_type val; +}; + +template +NBL_BOOL_CONCEPT SequenceSpecialization = concepts::UnsignedIntegral::scalar_type> && size_of_v::scalar_type> <= 4; +} + +// all Dim=1 +template NBL_PARTIAL_REQ_TOP(impl::SequenceSpecialization) +struct QuantizedSequence) > +{ + using this_t = QuantizedSequence; + using store_type = T; + using scalar_type = typename vector_traits::scalar_type; + NBL_CONSTEXPR_STATIC_INLINE uint16_t BitsPerComponent = 8u*size_of_v; + NBL_CONSTEXPR_STATIC_INLINE uint16_t Dimension = uint16_t(1u); + + static this_t create(const store_type value) + { + this_t seq; + seq.data = value; + return seq; + } + + store_type get(const uint16_t idx) { assert(idx >= 0 && idx < 1); return data; } + void set(const uint16_t idx, const store_type value) { assert(idx >= 0 && idx < 1); data = value; } + + template + static this_t encode(const vector value) + { + return impl::encode_helper::__call(value); + } + + template + vector decode(const vector,Dimension> scrambleKey) + { + impl::decode_before_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + template + vector decode(NBL_CONST_REF_ARG(this_t) scrambleKey) + { + impl::decode_after_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + + store_type data; +}; + +// uint16_t, uint32_t; Dim=2,3,4 +template NBL_PARTIAL_REQ_TOP(impl::SequenceSpecialization && vector_traits::Dimension == 1 && Dim > 1 && Dim < 5) +struct QuantizedSequence && vector_traits::Dimension == 1 && Dim > 1 && Dim < 5) > +{ + using this_t = QuantizedSequence; + using store_type = T; + using scalar_type = store_type; + NBL_CONSTEXPR_STATIC_INLINE uint16_t StoreBits = uint16_t(8u) * size_of_v; + NBL_CONSTEXPR_STATIC_INLINE uint16_t BitsPerComponent = StoreBits / Dim; + NBL_CONSTEXPR_STATIC_INLINE uint16_t DiscardBits = (uint16_t(8u) * size_of_v) - BitsPerComponent; + NBL_CONSTEXPR_STATIC_INLINE uint16_t Dimension = Dim; + + static this_t create(const vector value) + { + this_t seq; + seq.data = store_type(0u); + NBL_UNROLL for (uint16_t i = 0; i < Dimension; i++) + seq.set(i, value[i]); + return seq; + } + + store_type get(const uint16_t idx) + { + assert(idx >= 0 && idx < Dim); + return glsl::bitfieldExtract(data, BitsPerComponent * idx, BitsPerComponent); + } + + void set(const uint16_t idx, const store_type value) + { + assert(idx >= 0 && idx < Dim); + data = glsl::bitfieldInsert(data, scalar_type(value >> DiscardBits), BitsPerComponent * idx, BitsPerComponent); + } + + template + static this_t encode(const vector value) + { + return impl::encode_helper::__call(value); + } + + template + vector decode(const vector,Dimension> scrambleKey) + { + impl::decode_before_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + template + vector decode(NBL_CONST_REF_ARG(this_t) scrambleKey) + { + impl::decode_after_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + + store_type data; +}; + +// Dim 2,3,4 matches vector dim +template NBL_PARTIAL_REQ_TOP(impl::SequenceSpecialization && vector_traits::Dimension == Dim && Dim > 1 && Dim < 5) +struct QuantizedSequence && vector_traits::Dimension == Dim && Dim > 1 && Dim < 5) > +{ + using this_t = QuantizedSequence; + using store_type = T; + using scalar_type = typename vector_traits::scalar_type; + NBL_CONSTEXPR_STATIC_INLINE uint16_t BitsPerComponent = 8u*size_of_v; + NBL_CONSTEXPR_STATIC_INLINE uint16_t Dimension = Dim; + + static this_t create(const store_type value) + { + this_t seq; + seq.data = value; + return seq; + } + + scalar_type get(const uint16_t idx) { assert(idx >= 0 && idx < Dim); return data[idx]; } + void set(const uint16_t idx, const scalar_type value) { assert(idx >= 0 && idx < Dim); data[idx] = value; } + + template + static this_t encode(const vector value) + { + return impl::encode_helper::__call(value); + } + + template + vector decode(const vector,Dimension> scrambleKey) + { + impl::decode_before_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + template + vector decode(NBL_CONST_REF_ARG(this_t) scrambleKey) + { + impl::decode_after_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + + store_type data; +}; + +// uint32_t2; Dim=3 -- should never use uint16_t2 instead of uint32_t +template NBL_PARTIAL_REQ_TOP(is_same_v && Dim == 3) +struct QuantizedSequence && Dim == 3) > +{ + using this_t = QuantizedSequence; + using store_type = T; + using scalar_type = typename vector_traits::scalar_type; + NBL_CONSTEXPR_STATIC_INLINE uint16_t StoreBits = uint16_t(8u) * size_of_v; + NBL_CONSTEXPR_STATIC_INLINE uint16_t BitsPerComponent = StoreBits / Dim; + NBL_CONSTEXPR_STATIC_INLINE uint16_t DiscardBits = (uint16_t(8u) * size_of_v) - BitsPerComponent; + NBL_CONSTEXPR_STATIC_INLINE uint16_t Dimension = Dim; + + static this_t create(const vector value) + { + this_t seq; + seq.data = hlsl::promote(0u); + NBL_UNROLL for (uint16_t i = 0; i < Dimension; i++) + seq.set(i, value[i]); + return seq; + } + + scalar_type get(const uint16_t idx) + { + assert(idx >= 0 && idx < 3); + if (idx == 0) // x + return glsl::bitfieldExtract(data[0], 0u, BitsPerComponent); + else if (idx == 1) // y + { + scalar_type y = glsl::bitfieldExtract(data[0], BitsPerComponent, DiscardBits); + y |= glsl::bitfieldExtract(data[1], 0u, DiscardBits - 1u) << DiscardBits; + return y; + } + else // z + return glsl::bitfieldExtract(data[1], DiscardBits - 1u, BitsPerComponent); + } + + void set(const uint16_t idx, const scalar_type value) + { + assert(idx >= 0 && idx < 3); + const scalar_type trunc_val = value >> DiscardBits; + if (idx == 0) // x + data[0] = glsl::bitfieldInsert(data[0], trunc_val, 0u, BitsPerComponent); + else if (idx == 1) // y + { + data[0] = glsl::bitfieldInsert(data[0], trunc_val, BitsPerComponent, DiscardBits); + data[1] = glsl::bitfieldInsert(data[1], trunc_val >> DiscardBits, 0u, DiscardBits - 1u); + } + else // z + data[1] = glsl::bitfieldInsert(data[1], trunc_val, DiscardBits - 1u, BitsPerComponent); + } + + template + static this_t encode(const vector value) + { + return impl::encode_helper::__call(value); + } + + template + vector decode(const vector,Dimension> scrambleKey) + { + impl::decode_before_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + template + vector decode(NBL_CONST_REF_ARG(this_t) scrambleKey) + { + impl::decode_after_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + + store_type data; +}; + +// uint16_t2; Dim=4 -- should use uint16_t4 instead of uint32_t2 +template NBL_PARTIAL_REQ_TOP(is_same_v && Dim == 4) +struct QuantizedSequence && Dim == 4) > +{ + using this_t = QuantizedSequence; + using store_type = T; + using scalar_type = typename vector_traits::scalar_type; + NBL_CONSTEXPR_STATIC_INLINE uint16_t StoreBits = uint16_t(8u) * size_of_v; + NBL_CONSTEXPR_STATIC_INLINE uint16_t BitsPerComponent = StoreBits / Dim; + NBL_CONSTEXPR_STATIC_INLINE uint16_t DiscardBits = (uint16_t(8u) * size_of_v) - BitsPerComponent; + NBL_CONSTEXPR_STATIC_INLINE uint16_t Dimension = Dim; + + static this_t create(const vector value) + { + this_t seq; + seq.data = hlsl::promote(0u); + NBL_UNROLL for (uint16_t i = 0; i < Dimension; i++) + seq.set(i, value[i]); + return seq; + } + + scalar_type get(const uint16_t idx) + { + assert(idx >= 0 && idx < 4); + if (idx < 2) // x y + { + return glsl::bitfieldExtract(data[0], BitsPerComponent * idx, BitsPerComponent); + } + else // z w + { + return glsl::bitfieldExtract(data[1], BitsPerComponent * (idx - uint16_t(2u)), BitsPerComponent); + } + } + + void set(const uint16_t idx, const scalar_type value) + { + assert(idx >= 0 && idx < 4); + const scalar_type trunc_val = value >> DiscardBits; + if (idx < 2) // x y + { + data[0] = glsl::bitfieldInsert(data[0], trunc_val, BitsPerComponent * idx, BitsPerComponent); + } + else // z w + { + data[1] = glsl::bitfieldInsert(data[1], trunc_val, BitsPerComponent * (idx - uint16_t(2u)), BitsPerComponent); + } + } + + template + static this_t encode(const vector value) + { + return impl::encode_helper::__call(value); + } + + template + vector decode(const vector,Dimension> scrambleKey) + { + impl::decode_before_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + template + vector decode(NBL_CONST_REF_ARG(this_t) scrambleKey) + { + impl::decode_after_scramble_helper helper; + helper.val.data = data; + return helper(scrambleKey); + } + + store_type data; +}; + +// no uint16_t4, uint32_t4; Dim=2 + +} + +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl index f5c19fb864..f9e3d2f7ae 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl @@ -32,7 +32,7 @@ struct SphericalRectangle return retval; } - vector2_type generate(NBL_CONST_REF_ARG(vector2_type) rectangleExtents, NBL_CONST_REF_ARG(vector2_type) uv, NBL_REF_ARG(scalar_type) S) + vector2_type generate(const vector2_type rectangleExtents, const vector2_type uv, NBL_REF_ARG(scalar_type) S) { const vector4_type denorm_n_z = vector4_type(-rect.r0.y, rect.r0.x + rectangleExtents.x, rect.r0.y + rectangleExtents.y, -rect.r0.x); const vector4_type n_z = denorm_n_z / hlsl::sqrt(hlsl::promote(rect.r0.z * rect.r0.z) + denorm_n_z * denorm_n_z); diff --git a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl index 0c86b69793..5770403cd2 100644 --- a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl @@ -33,7 +33,7 @@ struct SphericalTriangle } // WARNING: can and will return NAN if one or three of the triangle edges are near zero length - vector3_type generate(scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector2_type) u) + vector3_type generate(scalar_type solidAngle, const vector3_type cos_vertices, const vector3_type sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, const vector2_type u) { scalar_type negSinSubSolidAngle,negCosSubSolidAngle; math::sincos(solidAngle * u.x - numbers::pi, negSinSubSolidAngle, negCosSubSolidAngle); @@ -51,7 +51,7 @@ struct SphericalTriangle { const scalar_type cosAngleAlongAC = ((v_ * q - u_ * p) * cos_vertices[0] - v_) / ((v_ * p + u_ * q) * sin_vertices[0]); if (nbl::hlsl::abs(cosAngleAlongAC) < 1.f) - C_s += math::quaternion_t::slerp_delta(tri.vertex0, tri.vertex2 * csc_b, cosAngleAlongAC); + C_s += math::quaternion::slerp_delta(tri.vertex0, tri.vertex2 * csc_b, cosAngleAlongAC); } vector3_type retval = tri.vertex1; @@ -61,12 +61,12 @@ struct SphericalTriangle { const scalar_type cosAngleAlongBC_s = nbl::hlsl::clamp(1.0 + cosBC_s * u.y - u.y, -1.f, 1.f); if (nbl::hlsl::abs(cosAngleAlongBC_s) < 1.f) - retval += math::quaternion_t::slerp_delta(tri.vertex1, C_s * csc_b_s, cosAngleAlongBC_s); + retval += math::quaternion::slerp_delta(tri.vertex1, C_s * csc_b_s, cosAngleAlongBC_s); } return retval; } - vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) u) + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, const vector2_type u) { scalar_type cos_a, cos_c, csc_b, csc_c; vector3_type cos_vertices, sin_vertices; @@ -76,7 +76,7 @@ struct SphericalTriangle return generate(rcpPdf, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); } - vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) L) + vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, scalar_type solidAngle, const vector3_type cos_vertices, const vector3_type sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, const vector3_type L) { pdf = 1.0 / solidAngle; @@ -102,7 +102,7 @@ struct SphericalTriangle return vector2_type(u,v); } - vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(vector3_type) L) + vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, const vector3_type L) { scalar_type cos_a, cos_c, csc_b, csc_c; vector3_type cos_vertices, sin_vertices; diff --git a/include/nbl/builtin/hlsl/sampling/uniform_spheres.hlsl b/include/nbl/builtin/hlsl/sampling/uniform_spheres.hlsl index df4100db9b..5fc3bc7a0b 100644 --- a/include/nbl/builtin/hlsl/sampling/uniform_spheres.hlsl +++ b/include/nbl/builtin/hlsl/sampling/uniform_spheres.hlsl @@ -23,7 +23,7 @@ struct UniformHemisphere using vector_t2 = vector; using vector_t3 = vector; - static vector_t3 generate(vector_t2 _sample) + static vector_t3 generate(const vector_t2 _sample) { T z = _sample.x; T r = hlsl::sqrt(hlsl::max(T(0.0), T(1.0) - z * z)); @@ -49,7 +49,7 @@ struct UniformSphere using vector_t2 = vector; using vector_t3 = vector; - static vector_t3 generate(vector_t2 _sample) + static vector_t3 generate(const vector_t2 _sample) { T z = T(1.0) - T(2.0) * _sample.x; T r = hlsl::sqrt(hlsl::max(T(0.0), T(1.0) - z * z)); diff --git a/include/nbl/builtin/hlsl/shapes/aabb.hlsl b/include/nbl/builtin/hlsl/shapes/aabb.hlsl index 5b4b1be39d..07219c6687 100644 --- a/include/nbl/builtin/hlsl/shapes/aabb.hlsl +++ b/include/nbl/builtin/hlsl/shapes/aabb.hlsl @@ -60,6 +60,7 @@ struct AABB point_t maxVx; }; + namespace util { namespace impl diff --git a/include/nbl/builtin/hlsl/shapes/obb.hlsl b/include/nbl/builtin/hlsl/shapes/obb.hlsl new file mode 100644 index 0000000000..bdddc48ebf --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/obb.hlsl @@ -0,0 +1,67 @@ +// 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_BUILTIN_HLSL_SHAPES_OBB_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_OBB_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace shapes +{ + +template +struct OBB +{ + using scalar_t = Scalar; + using point_t = vector; + + NBL_CONSTEXPR_STATIC_INLINE OBB create(point_t mid, point_t ext, const point_t axes[D]) + { + point_t obbScale = ext * 2.0f; + point_t axesScale[D]; + for (int dim_i = 0; dim_i < D; dim_i++) + { + axesScale[dim_i] = axes[dim_i] * obbScale[dim_i]; + } + OBB ret; + for (int row_i = 0; row_i < D; row_i++) + { + for (int col_i = 0; col_i < D; col_i++) + { + ret.transform[row_i][col_i] = axesScale[col_i][row_i]; + } + } + for (int dim_i = 0; dim_i < D; dim_i++) + { + scalar_t sum = 0; + for (int dim_j = 0; dim_j < D; dim_j++) + { + sum += axesScale[dim_j][dim_i]; + } + ret.transform[dim_i][D] = mid[dim_i] - (0.5 * sum); + } + return ret; + + } + + NBL_CONSTEXPR_STATIC_INLINE OBB createAxisAligned(point_t mid, point_t len) + { + point_t axes[D]; + for (auto dim_i = 0; dim_i < D; dim_i++) + { + axes[dim_i] = point_t(0); + axes[dim_i][dim_i] = 1; + } + return create(mid, len * 0.5f, axes); + } + + matrix transform; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl index daeb3175c3..11442bef7c 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_rectangle.hlsl @@ -25,14 +25,14 @@ struct SphericalRectangle using vector4_type = vector; using matrix3x3_type = matrix; - static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(matrix3x3_type) basis) + static SphericalRectangle create(const vector3_type observer, const vector3_type rectangleOrigin, const matrix3x3_type basis) { SphericalRectangle retval; retval.r0 = nbl::hlsl::mul(basis, rectangleOrigin - observer); return retval; } - static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N) + static SphericalRectangle create(const vector3_type observer, const vector3_type rectangleOrigin, const vector3_type T, vector3_type B, const vector3_type N) { SphericalRectangle retval; matrix3x3_type TBN = nbl::hlsl::transpose(matrix3x3_type(T, B, N)); @@ -40,7 +40,7 @@ struct SphericalRectangle return retval; } - scalar_type solidAngleOfRectangle(NBL_CONST_REF_ARG(vector) rectangleExtents) + scalar_type solidAngleOfRectangle(const vector rectangleExtents) { const vector4_type denorm_n_z = vector4_type(-r0.y, r0.x + rectangleExtents.x, r0.y + rectangleExtents.y, -r0.x); const vector4_type n_z = denorm_n_z / nbl::hlsl::sqrt((vector4_type)(r0.z * r0.z) + denorm_n_z * denorm_n_z); diff --git a/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl index f0b184d057..f574b106ce 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl @@ -25,7 +25,7 @@ struct SphericalTriangle using scalar_type = T; using vector3_type = vector; - static SphericalTriangle create(NBL_CONST_REF_ARG(vector3_type) vertex0, NBL_CONST_REF_ARG(vector3_type) vertex1, NBL_CONST_REF_ARG(vector3_type) vertex2, NBL_CONST_REF_ARG(vector3_type) origin) + static SphericalTriangle create(const vector3_type vertex0, const vector3_type vertex1, const vector3_type vertex2, const vector3_type origin) { SphericalTriangle retval; retval.vertex0 = nbl::hlsl::normalize(vertex0 - origin); @@ -72,7 +72,7 @@ struct SphericalTriangle return solidAngleOfTriangle(dummy0,dummy1,dummy2,dummy3,dummy4,dummy5); } - scalar_type projectedSolidAngleOfTriangle(NBL_CONST_REF_ARG(vector3_type) receiverNormal, NBL_REF_ARG(vector3_type) cos_sides, NBL_REF_ARG(vector3_type) csc_sides, NBL_REF_ARG(vector3_type) cos_vertices) + scalar_type projectedSolidAngleOfTriangle(const vector3_type receiverNormal, NBL_REF_ARG(vector3_type) cos_sides, NBL_REF_ARG(vector3_type) csc_sides, NBL_REF_ARG(vector3_type) cos_vertices) { if (pyramidAngles()) return 0.f; @@ -102,29 +102,6 @@ struct SphericalTriangle vector3_type csc_sides; }; -namespace util -{ - // Use this convetion e_i = v_{i+2}-v_{i+1}. vertex index is modulo by 3. - template - vector compInternalAngle(NBL_CONST_REF_ARG(vector) e0, NBL_CONST_REF_ARG(vector) e1, NBL_CONST_REF_ARG(vector) e2) - { - // Calculate this triangle's weight for each of its three m_vertices - // start by calculating the lengths of its sides - const float_t a = hlsl::dot(e0, e0); - const float_t asqrt = hlsl::sqrt(a); - const float_t b = hlsl::dot(e1, e1); - const float_t bsqrt = hlsl::sqrt(b); - const float_t c = hlsl::dot(e2, e2); - const float_t csqrt = hlsl::sqrt(c); - - const float_t angle0 = hlsl::acos((b + c - a) / (2.f * bsqrt * csqrt)); - const float_t angle1 = hlsl::acos((-b + c + a) / (2.f * asqrt * csqrt)); - const float_t angle2 = hlsl::numbers::pi - (angle0 + angle1); - // use them to find the angle at each vertex - return vector(angle0, angle1, angle2); - } -} - } } } diff --git a/include/nbl/builtin/hlsl/shapes/triangle.hlsl b/include/nbl/builtin/hlsl/shapes/triangle.hlsl new file mode 100644 index 0000000000..b2f4170f70 --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/triangle.hlsl @@ -0,0 +1,46 @@ +// 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_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ + +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace shapes +{ + +namespace util +{ +// Use this convetion e_i = v_{i+2}-v_{i+1}. vertex index is modulo by 3. +template +vector anglesFromTriangleEdges(const vector e0, vector e1, const vector e2) +{ + // Calculate this triangle's weight for each of its three m_vertices + // start by calculating the lengths of its sides + const float_t a = hlsl::dot(e0, e0); + const float_t asqrt = hlsl::sqrt(a); + const float_t b = hlsl::dot(e1, e1); + const float_t bsqrt = hlsl::sqrt(b); + const float_t c = hlsl::dot(e2, e2); + const float_t csqrt = hlsl::sqrt(c); + + const float_t angle0 = hlsl::acos((b + c - a) / (2.f * bsqrt * csqrt)); + const float_t angle1 = hlsl::acos((-b + c + a) / (2.f * asqrt * csqrt)); + const float_t angle2 = hlsl::numbers::pi - (angle0 + angle1); + // use them to find the angle at each vertex + return vector(angle0, angle1, angle2); +} +} + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl index a7614469dd..9190a4ec73 100644 --- a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl +++ b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl @@ -4,6 +4,8 @@ #ifndef _NBL_BUILTIN_HLSL_SPIRV_INTRINSICS_CORE_INCLUDED_ #define _NBL_BUILTIN_HLSL_SPIRV_INTRINSICS_CORE_INCLUDED_ +#include + #ifdef __HLSL_VERSION // TODO: AnastZIuk fix public search paths so we don't choke #include "spirv/unified1/spirv.hpp" @@ -11,7 +13,6 @@ #include #include #include -#include namespace nbl { @@ -115,7 +116,12 @@ NBL_CONSTEXPR_STATIC_INLINE bool is_bda_pointer_v = is_bda_pointer::value; //! General Operations - + +//! Miscellaneous Instructions +template +[[vk::ext_instruction(spv::OpUndef)]] +T undef(); + // template [[vk::ext_instruction(spv::OpAccessChain)]] @@ -382,7 +388,8 @@ template && (!conc [[vk::ext_instruction(spv::OpSelect)]] T select(U a, T x, T y); -NBL_VALID_EXPRESSION(SelectIsCallable, (T)(U), select(experimental::declval(),experimental::declval(),experimental::declval())); +// need to use `spirv::` even in the namespace because it matches the HLSL intrinsic which is not namespaced at all, and will happily match anything +NBL_VALID_EXPRESSION(SelectIsCallable, (T)(U), spirv::select(experimental::declval(),experimental::declval(),experimental::declval())); } diff --git a/include/nbl/builtin/hlsl/surface_transform.h b/include/nbl/builtin/hlsl/surface_transform.h index a681ecf0bb..0c9b54ce5a 100644 --- a/include/nbl/builtin/hlsl/surface_transform.h +++ b/include/nbl/builtin/hlsl/surface_transform.h @@ -4,6 +4,7 @@ #ifndef _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_INCLUDED_ #define _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_INCLUDED_ #include +#include #include namespace nbl @@ -64,7 +65,7 @@ inline float32_t2x2 transformMatrix(const FLAG_BITS transform) return float32_t2x2(_nan,_nan,_nan,_nan); } -//! [width,height] might switch to [height, width] in orientations such as 90°CW +//! [width,height] might switch to [height, width] in orientations such as 90° CW //! Usecase: Find out how big the viewport has to be after or before a tranform is applied inline uint16_t2 transformedExtents(const FLAG_BITS transform, const uint16_t2 screenSize) { @@ -93,7 +94,7 @@ inline float transformedAspectRatio(const FLAG_BITS transform, const uint16_t2 s } //! Use this function to apply the INVERSE of swapchain tranformation to the screenspace coordinate `coord` -//! For example when the device orientation is 90°CW then this transforms the point 90°CCW. +//! For example when the device orientation is 90° CW then this transforms the point 90° CCW. //! Usecase = [Gather]: //! Applications such as raytracing in shaders where you would want to generate rays from screen space coordinates. //! Warnings: @@ -174,11 +175,13 @@ inline float32_t2 applyToNDC(const FLAG_BITS transform, const float32_t2 ndc) template TwoColumns applyToDerivatives(const FLAG_BITS transform, TwoColumns dDx_dDy) { - using namespace glsl; // IN HLSL mode, C++ doens't need this to access `inverse` - return mul(inverse(transformMatrix(transform)),dDx_dDy); + return mul(::nbl::hlsl::inverse(transformMatrix(transform)),dDx_dDy); } } } } -#endif \ No newline at end of file + +#endif // _NBL_BUILTIN_HLSL_SURFACE_TRANSFORM_INCLUDED_ + + diff --git a/include/nbl/builtin/hlsl/testing/orientation_compare.hlsl b/include/nbl/builtin/hlsl/testing/orientation_compare.hlsl new file mode 100644 index 0000000000..3228872b4d --- /dev/null +++ b/include/nbl/builtin/hlsl/testing/orientation_compare.hlsl @@ -0,0 +1,46 @@ +#ifndef _NBL_BUILTIN_HLSL_TESTING_ORIENTATION_COMPARE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_TESTING_ORIENTATION_COMPARE_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace testing +{ +namespace impl +{ + +template) +struct OrientationCompareHelper +{ + static bool __call(NBL_CONST_REF_ARG(FloatingPointVector) lhs, NBL_CONST_REF_ARG(FloatingPointVector) rhs, const float64_t maxAllowedDifference) + { + using traits = nbl::hlsl::vector_traits; + using scalar_t = typename traits::scalar_type; + + const scalar_t dotLR = hlsl::abs(hlsl::dot(lhs, rhs)); + const scalar_t dotLL = hlsl::dot(lhs,lhs); + const scalar_t dotRR = hlsl::dot(rhs,rhs); + if (dotLL < numeric_limits::min || dotRR < numeric_limits::min) + return false; + + const scalar_t scale = hlsl::sqrt(dotLL * dotRR); + return relativeApproxCompare(dotLR, scale, maxAllowedDifference); + } +}; + +} + +template +bool orientationCompare(NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs, const float64_t maxAllowedDifference) +{ + return impl::OrientationCompareHelper::__call(lhs, rhs, maxAllowedDifference); +} + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/testing/relative_approx_compare.hlsl b/include/nbl/builtin/hlsl/testing/relative_approx_compare.hlsl new file mode 100644 index 0000000000..8d32780f93 --- /dev/null +++ b/include/nbl/builtin/hlsl/testing/relative_approx_compare.hlsl @@ -0,0 +1,94 @@ +#ifndef _NBL_BUILTIN_HLSL_TESTING_RELATIVE_APPROX_COMPARE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_TESTING_RELATIVE_APPROX_COMPARE_INCLUDED_ + +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace testing +{ +namespace impl +{ + +template +struct RelativeApproxCompareHelper; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeScalar) +struct RelativeApproxCompareHelper) > +{ + static bool __call(NBL_CONST_REF_ARG(FloatingPoint) lhs, NBL_CONST_REF_ARG(FloatingPoint) rhs, const float64_t maxAllowedDifference) + { + const bool bothAreNaN = nbl::hlsl::isnan(lhs) && nbl::hlsl::isnan(rhs); + const bool bothAreInf = nbl::hlsl::isinf(lhs) && nbl::hlsl::isinf(rhs); + const bool bothHaveSameSign = nbl::hlsl::ieee754::extractSign(lhs) == nbl::hlsl::ieee754::extractSign(rhs); + const bool lhsIsSubnormalOrZero = ieee754::isSubnormal(lhs) || ieee754::isZero(lhs); + const bool rhsIsSubnormalOrZero = ieee754::isSubnormal(rhs) || ieee754::isZero(rhs); + + if (bothAreNaN) + return true; + if (bothAreInf && bothHaveSameSign) + return true; + if (lhsIsSubnormalOrZero && rhsIsSubnormalOrZero) + return true; + if (!lhsIsSubnormalOrZero && rhsIsSubnormalOrZero) + return false; + if (lhsIsSubnormalOrZero && !rhsIsSubnormalOrZero) + return false; + + return hlsl::max(hlsl::abs(lhs / rhs), hlsl::abs(rhs / lhs)) <= 1.f + maxAllowedDifference; + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) +struct RelativeApproxCompareHelper) > +{ + static bool __call(NBL_CONST_REF_ARG(FloatingPointVector) lhs, NBL_CONST_REF_ARG(FloatingPointVector) rhs, const float64_t maxAllowedDifference) + { + using traits = nbl::hlsl::vector_traits; + for (uint32_t i = 0; i < traits::Dimension; ++i) + { + if (!RelativeApproxCompareHelper::__call(lhs[i], rhs[i], maxAllowedDifference)) + return false; + } + + return true; + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::Matricial && concepts::FloatingPointLikeScalar::scalar_type>) +struct RelativeApproxCompareHelper && concepts::FloatingPointLikeScalar::scalar_type>) > +{ + static bool __call(NBL_CONST_REF_ARG(FloatingPointMatrix) lhs, NBL_CONST_REF_ARG(FloatingPointMatrix) rhs, const float64_t maxAllowedDifference) + { + using traits = nbl::hlsl::matrix_traits; + for (uint32_t i = 0; i < traits::RowCount; ++i) + { + if (!RelativeApproxCompareHelper::__call(lhs[i], rhs[i], maxAllowedDifference)) + return false; + } + + return true; + } +}; + +} + +template +bool relativeApproxCompare(NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs, const float64_t maxAllowedDifference) +{ + return impl::RelativeApproxCompareHelper::__call(lhs, rhs, maxAllowedDifference); +} + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/testing/vector_length_compare.hlsl b/include/nbl/builtin/hlsl/testing/vector_length_compare.hlsl new file mode 100644 index 0000000000..03bf72b006 --- /dev/null +++ b/include/nbl/builtin/hlsl/testing/vector_length_compare.hlsl @@ -0,0 +1,45 @@ +#ifndef _NBL_BUILTIN_HLSL_TESTING_VECTOR_LENGTH_COMPARE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_TESTING_VECTOR_LENGTH_COMPARE_INCLUDED_ + +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace testing +{ +namespace impl +{ + +template) +struct LengthCompareHelper +{ + static bool __call(NBL_CONST_REF_ARG(FloatingPointVector) lhs, NBL_CONST_REF_ARG(FloatingPointVector) rhs, const float64_t maxAbsoluteDifference, const float64_t maxRelativeDifference) + { + using traits = nbl::hlsl::vector_traits; + using scalar_t = typename traits::scalar_type; + + const scalar_t dotLL = hlsl::dot(lhs,lhs); + const scalar_t dotRR = hlsl::dot(rhs,rhs); + const scalar_t diff = hlsl::abs(dotLL-dotRR); + const scalar_t sc = hlsl::max(dotLL,dotRR); + return diff <= maxAbsoluteDifference || diff <= maxRelativeDifference*sc; + } +}; + +} + +template +bool vectorLengthCompare(NBL_CONST_REF_ARG(T) lhs, NBL_CONST_REF_ARG(T) rhs, const float64_t maxAbsoluteDifference, const float64_t maxRelativeDifference) +{ + return impl::LengthCompareHelper::__call(lhs, rhs, maxAbsoluteDifference, maxRelativeDifference); +} + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/type_traits.hlsl b/include/nbl/builtin/hlsl/type_traits.hlsl index a9701619dd..257a753129 100644 --- a/include/nbl/builtin/hlsl/type_traits.hlsl +++ b/include/nbl/builtin/hlsl/type_traits.hlsl @@ -636,28 +636,39 @@ template using conditional_t = typename conditional::type; -// Template variables +// Template Variables +template +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR T integral_constant_v = integral_constant::value; template -NBL_CONSTEXPR bool is_same_v = is_same::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_same_v = is_same::value; template -NBL_CONSTEXPR bool is_unsigned_v = is_unsigned::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_unsigned_v = is_unsigned::value; template -NBL_CONSTEXPR bool is_integral_v = is_integral::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_integral_v = is_integral::value; template -NBL_CONSTEXPR bool is_floating_point_v = is_floating_point::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_floating_point_v = is_floating_point::value; template -NBL_CONSTEXPR bool is_signed_v = is_signed::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_signed_v = is_signed::value; template -NBL_CONSTEXPR bool is_scalar_v = is_scalar::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_scalar_v = is_scalar::value; template -NBL_CONSTEXPR uint64_t size_of_v = size_of::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint64_t size_of_v = size_of::value; template -NBL_CONSTEXPR uint32_t alignment_of_v = alignment_of::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t alignment_of_v = alignment_of::value; +template +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_fundamental_v = is_fundamental::value; + // Overlapping definitions template using make_void_t = typename make_void::type; +template +using make_signed_t = typename make_signed::type; + +template +using make_unsigned_t = typename make_unsigned::type; + template struct conditional_value { @@ -674,7 +685,7 @@ template struct is_vector > : bool_constant {}; template -NBL_CONSTEXPR bool is_vector_v = is_vector::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_vector_v = is_vector::value; #ifndef __HLSL_VERSION template @@ -685,7 +696,7 @@ template struct is_matrix > : bool_constant {}; template -NBL_CONSTEXPR bool is_matrix_v = is_matrix::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_matrix_v = is_matrix::value; template @@ -721,16 +732,16 @@ struct extent : integral_constant::value> {}; template struct extent : integral_constant::value> {}; -template -struct extent, 0> : integral_constant {}; +template +struct extent, I> : extent {}; template -struct extent, I> : integral_constant::value> {}; +struct extent, I> : extent {}; // Template Variables template -NBL_CONSTEXPR uint64_t extent_v = extent::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint64_t extent_v = extent::value; template::value> @@ -844,15 +855,6 @@ struct float_of_size<8> template using float_of_size_t = typename float_of_size::type; -template -struct extent, 0> : integral_constant {}; - -template -struct extent, 0> : integral_constant {}; - -template -struct extent, 1> : integral_constant {}; - } } diff --git a/include/nbl/builtin/hlsl/vector_utils/vector_traits.hlsl b/include/nbl/builtin/hlsl/vector_utils/vector_traits.hlsl index 652cabd7c7..95315f6e3c 100644 --- a/include/nbl/builtin/hlsl/vector_utils/vector_traits.hlsl +++ b/include/nbl/builtin/hlsl/vector_utils/vector_traits.hlsl @@ -18,20 +18,13 @@ struct vector_traits NBL_CONSTEXPR_STATIC_INLINE bool IsVector = false; }; -// i choose to implement it this way because of this DXC bug: https://github.com/microsoft/DirectXShaderCom0piler/issues/7007 -#define DEFINE_VECTOR_TRAITS_TEMPLATE_SPECIALIZATION(DIMENSION)\ -template \ -struct vector_traits >\ -{\ - using scalar_type = T;\ - NBL_CONSTEXPR_STATIC_INLINE uint32_t Dimension = DIMENSION;\ - NBL_CONSTEXPR_STATIC_INLINE bool IsVector = true;\ -};\ - -DEFINE_VECTOR_TRAITS_TEMPLATE_SPECIALIZATION(1) -DEFINE_VECTOR_TRAITS_TEMPLATE_SPECIALIZATION(2) -DEFINE_VECTOR_TRAITS_TEMPLATE_SPECIALIZATION(3) -DEFINE_VECTOR_TRAITS_TEMPLATE_SPECIALIZATION(4) +template +struct vector_traits > +{ + using scalar_type = T; + NBL_CONSTEXPR_STATIC_INLINE uint32_t Dimension = N; + NBL_CONSTEXPR_STATIC_INLINE bool IsVector = true; +}; } } diff --git a/include/nbl/builtin/hlsl/workgroup2/arithmetic_config.hlsl b/include/nbl/builtin/hlsl/workgroup2/arithmetic_config.hlsl index 03ccd64d4e..22c93ce193 100644 --- a/include/nbl/builtin/hlsl/workgroup2/arithmetic_config.hlsl +++ b/include/nbl/builtin/hlsl/workgroup2/arithmetic_config.hlsl @@ -225,7 +225,7 @@ template struct is_configuration > : bool_constant {}; template -NBL_CONSTEXPR bool is_configuration_v = is_configuration::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_configuration_v = is_configuration::value; } } diff --git a/include/nbl/config/BuildConfigOptions.h.in b/include/nbl/config/BuildConfigOptions.h.in index c67c942217..d130ff4ce2 100644 --- a/include/nbl/config/BuildConfigOptions.h.in +++ b/include/nbl/config/BuildConfigOptions.h.in @@ -59,6 +59,8 @@ #cmakedefine _NBL_BUILD_DPL_ +#cmakedefine NBL_BUILD_DEBUG_DRAW + // ! // TODO: This has to disapppear from the main header and go to the OptiX extension header + config #cmakedefine OPTIX_INCLUDE_DIR "@OPTIX_INCLUDE_DIR@" diff --git a/include/nbl/core/algorithm/utility.h b/include/nbl/core/algorithm/utility.h index 136f1047cc..2ad3920839 100644 --- a/include/nbl/core/algorithm/utility.h +++ b/include/nbl/core/algorithm/utility.h @@ -18,6 +18,30 @@ struct type_list_size> : std::integral_constant inline constexpr size_t type_list_size_v = type_list_size::value; +template class, typename TypeList> +struct filter; +template class Pred, typename... T> +struct filter> +{ + using type = type_list<>; +}; + +template class Pred, typename T, typename... Ts> +struct filter> +{ + template + struct Cons; + template + struct Cons> + { + using type = type_list; + }; + + using type = std::conditional_t::value,typename Cons>::type>::type,typename filter>::type>; +}; +template class Pred, typename TypeList> +using filter_t = filter::type; + template class ListLikeOutT, template class X, typename ListLike> struct list_transform { diff --git a/include/nbl/core/containers/CMemoryPool.h b/include/nbl/core/containers/CMemoryPool.h index 85488d8e7d..737848ebff 100644 --- a/include/nbl/core/containers/CMemoryPool.h +++ b/include/nbl/core/containers/CMemoryPool.h @@ -1,5 +1,6 @@ -#ifndef __NBL_C_MEMORY_POOL_H_INCLUDED__ -#define __NBL_C_MEMORY_POOL_H_INCLUDED__ +#ifndef _NBL_CORE_C_MEMORY_POOL_H_INCLUDED_ +#define _NBL_CORE_C_MEMORY_POOL_H_INCLUDED_ + #include "nbl/core/decl/compile_config.h" #include "nbl/core/alloc/SimpleBlockBasedAllocator.h" @@ -8,9 +9,11 @@ #include #include + namespace nbl::core { +// TODO: change DataAllocator to PMR template class DataAllocator, bool isThreadSafe, typename... Args> class CMemoryPool : public Uncopyable { diff --git a/include/nbl/core/declarations.h b/include/nbl/core/declarations.h index 9aa708a793..466ea988aa 100644 --- a/include/nbl/core/declarations.h +++ b/include/nbl/core/declarations.h @@ -50,7 +50,6 @@ #include "nbl/core/math/colorutil.h" #include "nbl/core/math/rational.h" #include "nbl/core/math/plane3dSIMD.h" -#include "nbl/core/math/matrixutil.h" // memory #include "nbl/core/memory/memory.h" #include "nbl/core/memory/new_delete.h" diff --git a/include/nbl/core/definitions.h b/include/nbl/core/definitions.h index c08af6ad74..5913c2c8f2 100644 --- a/include/nbl/core/definitions.h +++ b/include/nbl/core/definitions.h @@ -15,8 +15,4 @@ #include "nbl/core/math/floatutil.tcc" #include "nbl/core/math/glslFunctions.tcc" -// implementations [deprecated] -#include "matrix3x4SIMD_impl.h" -#include "matrix4SIMD_impl.h" - #endif \ No newline at end of file diff --git a/include/nbl/core/hash/blake.h b/include/nbl/core/hash/blake.h index 801b867766..fb91c9969f 100644 --- a/include/nbl/core/hash/blake.h +++ b/include/nbl/core/hash/blake.h @@ -4,10 +4,13 @@ #ifndef _NBL_CORE_HASH_BLAKE3_H_INCLUDED_ #define _NBL_CORE_HASH_BLAKE3_H_INCLUDED_ + +#include "nbl/config/BuildConfigOptions.h" #include "blake3.h" #include + namespace nbl::core { struct blake3_hash_t final diff --git a/include/nbl/core/math/floatutil.tcc b/include/nbl/core/math/floatutil.tcc index 71c8bd2da7..f20db5dec2 100644 --- a/include/nbl/core/math/floatutil.tcc +++ b/include/nbl/core/math/floatutil.tcc @@ -5,9 +5,8 @@ #ifndef __NBL_CORE_FLOAT_UTIL_TCC_INCLUDED__ #define __NBL_CORE_FLOAT_UTIL_TCC_INCLUDED__ - +#include "vectorSIMD.h" #include "nbl/core/math/floatutil.h" -#include "matrix4SIMD.h" namespace nbl { @@ -29,16 +28,6 @@ NBL_FORCE_INLINE vectorSIMDf ROUNDING_ERROR() { return vectorSIMDf(ROUNDING_ERROR()); } -template<> -NBL_FORCE_INLINE matrix3x4SIMD ROUNDING_ERROR() -{ - return matrix3x4SIMD(ROUNDING_ERROR(),ROUNDING_ERROR(),ROUNDING_ERROR()); -} -template<> -NBL_FORCE_INLINE matrix4SIMD ROUNDING_ERROR() -{ - return matrix4SIMD(ROUNDING_ERROR(),ROUNDING_ERROR(),ROUNDING_ERROR(),ROUNDING_ERROR()); -} template NBL_FORCE_INLINE T ROUNDING_ERROR() { diff --git a/include/nbl/core/math/glslFunctions.tcc b/include/nbl/core/math/glslFunctions.tcc index 205585965b..b8326b41d1 100644 --- a/include/nbl/core/math/glslFunctions.tcc +++ b/include/nbl/core/math/glslFunctions.tcc @@ -8,7 +8,6 @@ #include "nbl/core/declarations.h" #include "nbl/core/math/floatutil.tcc" -#include "matrix4SIMD.h" #include #include @@ -280,21 +279,6 @@ NBL_FORCE_INLINE vectorSIMDf cross(const vectorSIMDf& a, const vect #endif } -template<> -NBL_FORCE_INLINE matrix4SIMD transpose(const matrix4SIMD& m) -{ - core::matrix4SIMD retval; - __m128 a0 = m.rows[0].getAsRegister(), a1 = m.rows[1].getAsRegister(), a2 = m.rows[2].getAsRegister(), a3 = m.rows[3].getAsRegister(); - _MM_TRANSPOSE4_PS(a0, a1, a2, a3); - retval.rows[0] = a0; - retval.rows[1] = a1; - retval.rows[2] = a2; - retval.rows[3] = a3; - return retval; -} - - - template<> NBL_FORCE_INLINE bool equals(const vectorSIMDf& a, const vectorSIMDf& b, const vectorSIMDf& tolerance) { @@ -307,22 +291,6 @@ NBL_FORCE_INLINE bool equals(const core::vector3df& a, const core::vector3df& b, auto la = a-tolerance; return ha.X>=b.X&&ha.Y>=b.Y&&ha.Z>=b.Z && la.X<=b.X&&la.Y<=b.Y&&la.Z<=b.Z; } -template<> -NBL_FORCE_INLINE bool equals(const matrix4SIMD& a, const matrix4SIMD& b, const matrix4SIMD& tolerance) -{ - for (size_t i = 0u; i(a.rows[i], b.rows[i], tolerance.rows[i])) - return false; - return true; -} -template<> -NBL_FORCE_INLINE bool equals(const matrix3x4SIMD& a, const matrix3x4SIMD& b, const matrix3x4SIMD& tolerance) -{ - for (size_t i = 0u; i(a.rows[i], b.rows[i], tolerance[i])) - return false; - return true; -} template NBL_FORCE_INLINE bool equals(const T& a, const T& b, const T& tolerance) { diff --git a/include/nbl/core/math/matrixutil.h b/include/nbl/core/math/matrixutil.h deleted file mode 100644 index afe7955c9b..0000000000 --- a/include/nbl/core/math/matrixutil.h +++ /dev/null @@ -1,29 +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_MATRIX_UTIL_H_INCLUDED_ -#define _NBL_MATRIX_UTIL_H_INCLUDED_ - -#include "matrix4SIMD.h" -#include "matrix3x4SIMD.h" - -namespace nbl::core -{ - - -//! TODO: OPTIMIZE THIS, DON'T PROMOTE THE MATRIX IF DON'T HAVE TO -inline matrix4SIMD concatenateBFollowedByA(const matrix4SIMD& _a, const matrix3x4SIMD& _b) -{ - return concatenateBFollowedByA(_a, matrix4SIMD(_b)); -} -/* -inline matrix4SIMD concatenateBFollowedByAPrecisely(const matrix4SIMD& _a, const matrix3x4SIMD& _b) -{ - return concatenateBFollowedByAPrecisely(_a, matrix4SIMD(_b)); -} -*/ - -} - -#endif diff --git a/include/nbl/core/math/plane3dSIMD.h b/include/nbl/core/math/plane3dSIMD.h index 891ed1300c..25451553c1 100644 --- a/include/nbl/core/math/plane3dSIMD.h +++ b/include/nbl/core/math/plane3dSIMD.h @@ -3,11 +3,12 @@ // For conditions of distribution and use, see copyright notice in nabla.h // See the original file in irrlicht source for authors +#include "vectorSIMD.h" +#include + #ifndef __NBL_CORE_PLANE_3D_H_INCLUDED__ #define __NBL_CORE_PLANE_3D_H_INCLUDED__ -#include "matrix3x4SIMD.h" - namespace nbl { namespace core @@ -99,14 +100,21 @@ class plane3dSIMDf : private vectorSIMDf } //! - static inline plane3dSIMDf transform(const plane3dSIMDf& _in, const matrix3x4SIMD& _mat) + static inline plane3dSIMDf transform(const plane3dSIMDf& _in, const hlsl::float32_t3x4& _mat) { - matrix3x4SIMD inv; - _mat.getInverse(inv); + hlsl::float32_t4x4 inv = hlsl::inverse(hlsl::math::linalg::promote_affine<4, 4, 3, 4>(_mat)); vectorSIMDf normal(_in.getNormal()); // transform by inverse transpose - return plane3dSIMDf(inv.rows[0]*normal.xxxx()+inv.rows[1]*normal.yyyy()+inv.rows[2]*normal.zzzz()+(normal.wwww()&BUILD_MASKF(0,0,0,1))); + hlsl::float32_t4 planeEq = inv[0] * hlsl::float32_t4(normal.x, normal.x, normal.x, normal.x) + + inv[1] * hlsl::float32_t4(normal.y, normal.y, normal.y, normal.y) + + inv[2] * hlsl::float32_t4(normal.z, normal.z, normal.z, normal.z) + + (hlsl::float32_t4(0, 0, 0, normal.w)); + vectorSIMDf planeEqSIMD; + for (int i = 0; i < 4; ++i) + planeEqSIMD[i] = planeEq[i]; + + return plane3dSIMDf(planeEqSIMD); #undef BUILD_MASKF } diff --git a/include/nbl/core/sampling/RandomSampler.h b/include/nbl/core/sampling/RandomSampler.h index 39832dc8f1..b692ef5e08 100644 --- a/include/nbl/core/sampling/RandomSampler.h +++ b/include/nbl/core/sampling/RandomSampler.h @@ -11,8 +11,8 @@ namespace nbl::core { -class RandomSampler -{ + class RandomSampler + { public: RandomSampler(uint32_t _seed) { @@ -25,9 +25,24 @@ class RandomSampler return mersenneTwister(); } + // Returns a float in [0, 1) + inline float nextFloat() + { + // 1 / 2^32 + constexpr float norm = 1.0f / 4294967296.0f; + return mersenneTwister() * norm; + } + + // Returns a float in [min, max) + inline float nextFloat(float min, float max) + { + constexpr float norm = 1.0f / 4294967296.0f; + return min + (mersenneTwister() * norm) * (max - min); + } + protected: std::mt19937 mersenneTwister; -}; + }; } diff --git a/include/nbl/core/string/stringutil.h b/include/nbl/core/string/stringutil.h index 3183ef149b..0eb9628250 100644 --- a/include/nbl/core/string/stringutil.h +++ b/include/nbl/core/string/stringutil.h @@ -180,7 +180,7 @@ namespace core //! DOCUMENTATION TODO struct CaseInsensitiveHash { - inline std::size_t operator()(const std::string& val) const + inline std::size_t operator()(const std::string_view val) const { std::size_t seed = 0; for (auto it = val.begin(); it != val.end(); it++) @@ -192,7 +192,7 @@ namespace core }; struct CaseInsensitiveEquals { - inline bool operator()(const std::string& A, const std::string& B) const + inline bool operator()(const std::string_view A, const std::string_view B) const { return core::strcmpi(A, B)==0; } diff --git a/include/nbl/ext/Bullet/BulletUtility.h b/include/nbl/ext/Bullet/BulletUtility.h index 507adbceda..450c20c50d 100644 --- a/include/nbl/ext/Bullet/BulletUtility.h +++ b/include/nbl/ext/Bullet/BulletUtility.h @@ -64,8 +64,8 @@ namespace Bullet3 return convert(vec); } - inline core::matrix3x4SIMD convertbtTransform(const btTransform &trans) { - core::matrix3x4SIMD mat; + inline hlsl::float32_t3x4 convertbtTransform(const btTransform &trans) { + hlsl::float32_t3x4 mat; for (uint32_t i = 0; i < 3u; ++i) { mat.rows[i] = frombtVec3(trans.getBasis().getRow(i)); @@ -75,7 +75,7 @@ namespace Bullet3 return mat; } - inline btTransform convertMatrixSIMD(const core::matrix3x4SIMD &mat) { + inline btTransform convertMatrixSIMD(const hlsl::float32_t3x4 &mat) { btTransform transform; //Calling makeSafe3D on rows erases translation so save it diff --git a/include/nbl/ext/Bullet/CPhysicsWorld.h b/include/nbl/ext/Bullet/CPhysicsWorld.h index d6529a2565..cfaf70d6d6 100644 --- a/include/nbl/ext/Bullet/CPhysicsWorld.h +++ b/include/nbl/ext/Bullet/CPhysicsWorld.h @@ -24,7 +24,7 @@ class CPhysicsWorld : public core::IReferenceCounted struct RigidBodyData { btCollisionShape *shape; - core::matrix3x4SIMD trans; + hlsl::float32_t3x4 trans; core::vectorSIMDf inertia; float mass; }; diff --git a/include/nbl/ext/DebugDraw/CDraw3DLine.h b/include/nbl/ext/DebugDraw/CDraw3DLine.h index 68cd64e9c1..86b874f9d1 100644 --- a/include/nbl/ext/DebugDraw/CDraw3DLine.h +++ b/include/nbl/ext/DebugDraw/CDraw3DLine.h @@ -33,7 +33,7 @@ class CDraw3DLine : public core::IReferenceCounted } - void setData(const core::matrix4SIMD& viewProjMat, const core::vector>& linesData) + void setData(const hlsl::float32_t4x4& viewProjMat, const core::vector>& linesData) { m_viewProj = viewProjMat; m_lines = linesData; @@ -45,7 +45,7 @@ class CDraw3DLine : public core::IReferenceCounted m_lines.clear(); } - void setLine(const core::matrix4SIMD& viewProjMat, + void setLine(const hlsl::float32_t4x4& viewProjMat, float fromX, float fromY, float fromZ, float toX, float toY, float toZ, float r, float g, float b, float a @@ -54,7 +54,7 @@ class CDraw3DLine : public core::IReferenceCounted m_lines = core::vector>{ std::pair(S3DLineVertex{{ fromX, fromY, fromZ }, { r, g, b, a }}, S3DLineVertex{{ toX, toY, toZ }, { r, g, b, a }}) }; } - void addLine(const core::matrix4SIMD& viewProjMat, + void addLine(const hlsl::float32_t4x4& viewProjMat, float fromX, float fromY, float fromZ, float toX, float toY, float toZ, float r, float g, float b, float a @@ -73,7 +73,7 @@ class CDraw3DLine : public core::IReferenceCounted m_lines.insert(m_lines.end(), linesData.begin(), linesData.end()); } - void setViewProjMatrix(const core::matrix4SIMD& viewProjMat) + void setViewProjMatrix(const hlsl::float32_t4x4& viewProjMat) { m_viewProj = viewProjMat; } @@ -91,7 +91,7 @@ class CDraw3DLine : public core::IReferenceCounted */ void recordToCommandBuffer(video::IGPUCommandBuffer* cmdBuffer, video::IGPUGraphicsPipeline* graphics_pipeline); - inline void addBox(const core::aabbox3df& box, float r, float g, float b, float a, const core::matrix3x4SIMD& tform=core::matrix3x4SIMD()) + inline void addBox(const core::aabbox3df& box, float r, float g, float b, float a, const hlsl::float32_t3x4& tform=hlsl::float32_t3x4()) { auto addLine = [&](auto s, auto e) -> void { @@ -128,7 +128,7 @@ class CDraw3DLine : public core::IReferenceCounted core::smart_refctd_ptr m_device; core::smart_refctd_ptr m_linesBuffer = nullptr; core::smart_refctd_ptr m_rpindependent_pipeline; - core::matrix4SIMD m_viewProj; + hlsl::float32_t4x4 m_viewProj; core::vector> m_lines; const uint32_t alignments[1] = { sizeof(S3DLineVertex) }; }; diff --git a/include/nbl/ext/DebugDraw/CDrawAABB.h b/include/nbl/ext/DebugDraw/CDrawAABB.h new file mode 100644 index 0000000000..126731f425 --- /dev/null +++ b/include/nbl/ext/DebugDraw/CDrawAABB.h @@ -0,0 +1,242 @@ +// 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_DEBUG_DRAW_DRAW_AABB_H_ +#define _NBL_EXT_DEBUG_DRAW_DRAW_AABB_H_ + +#include "nbl/video/declarations.h" +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/shapes/aabb.hlsl" +#include "nbl/builtin/hlsl/math/linalg/fast_affine.hlsl" +#include "nbl/ext/DebugDraw/builtin/hlsl/common.hlsl" + +namespace nbl::ext::debug_draw +{ + class DrawAABB final : public core::IReferenceCounted + { + public: + static constexpr inline uint32_t IndicesCount = 24u; + + enum DrawMode : uint16_t + { + ADM_DRAW_SINGLE = 0b01, + ADM_DRAW_BATCH = 0b10, + ADM_DRAW_BOTH = 0b11 + }; + + struct SCachedCreationParameters + { + using streaming_buffer_t = video::StreamingTransientDataBufferST>; + + static constexpr inline auto RequiredAllocateFlags = core::bitflag(video::IDeviceMemoryAllocation::EMAF_DEVICE_ADDRESS_BIT); + static constexpr inline auto RequiredUsageFlags = core::bitflag(asset::IBuffer::EUF_STORAGE_BUFFER_BIT) | asset::IBuffer::EUF_SHADER_DEVICE_ADDRESS_BIT; + + DrawMode drawMode = ADM_DRAW_BOTH; + + core::smart_refctd_ptr utilities; + + //! optional, default MDI buffer allocated if not provided + core::smart_refctd_ptr streamingBuffer = nullptr; + }; + + struct SCreationParameters : SCachedCreationParameters + { + video::IQueue* transfer = nullptr; // only used to make the 24 element index buffer and instanced pipeline on create + core::smart_refctd_ptr assetManager = nullptr; + + core::smart_refctd_ptr singlePipelineLayout = nullptr; + core::smart_refctd_ptr batchPipelineLayout = nullptr; + core::smart_refctd_ptr renderpass = nullptr; + + inline bool validate() const + { + const auto validation = std::to_array + ({ + std::make_pair(bool(assetManager), "Invalid `creationParams.assetManager` is nullptr!"), + std::make_pair(bool(utilities), "Invalid `creationParams.utilities` is nullptr!"), + std::make_pair(bool(transfer), "Invalid `creationParams.transfer` is nullptr!"), + std::make_pair(bool(renderpass), "Invalid `creationParams.renderpass` is nullptr!"), + std::make_pair(bool(utilities->getLogicalDevice()->getPhysicalDevice()->getQueueFamilyProperties()[transfer->getFamilyIndex()].queueFlags.hasFlags(video::IQueue::FAMILY_FLAGS::TRANSFER_BIT)), "Invalid `creationParams.transfer` is not capable of transfer operations!") + }); + + system::logger_opt_ptr logger = utilities->getLogger(); + for (const auto& [ok, error] : validation) + if (!ok) + { + logger.log(error, system::ILogger::ELL_ERROR); + return false; + } + + assert(bool(assetManager->getSystem())); + + return true; + } + }; + + struct DrawParameters + { + video::IGPUCommandBuffer* commandBuffer = nullptr; + hlsl::float32_t4x4 cameraMat; + float lineWidth = 1.f; + }; + + // creates an instance that can draw one AABB via push constant or multiple using streaming buffer + static core::smart_refctd_ptr create(SCreationParameters&& params); + + // creates pipeline layout from push constant range + static core::smart_refctd_ptr createPipelineLayoutFromPCRange(video::ILogicalDevice* device, const asset::SPushConstantRange& pcRange); + + // creates default pipeline layout for pipeline specified by draw mode (note: if mode==BOTH, returns layout for BATCH mode) + static core::smart_refctd_ptr createDefaultPipelineLayout(video::ILogicalDevice* device, DrawMode mode = ADM_DRAW_BATCH); + + //! mounts the extension's archive to given system - useful if you want to create your own shaders with common header included + static const core::smart_refctd_ptr mount(core::smart_refctd_ptr logger, system::ISystem* system, video::ILogicalDevice* device, const std::string_view archiveAlias = ""); + + inline const SCachedCreationParameters& getCreationParameters() const { return m_cachedCreationParams; } + + // records draw command for single AABB, user has to set pipeline outside + bool renderSingle(const DrawParameters& params, const hlsl::shapes::AABB<3, float>& aabb, const hlsl::float32_t4& color); + + // records draw command for rendering batch of AABB instances as InstanceData + // user has to set span of filled-in InstanceData; camera matrix used in push constant + inline bool render(const DrawParameters& params, video::ISemaphore::SWaitInfo waitInfo, std::span aabbInstances) + { + system::logger_opt_ptr logger = m_cachedCreationParams.utilities->getLogger(); + if (!(m_cachedCreationParams.drawMode & ADM_DRAW_BATCH)) + { + logger.log("DrawAABB has not been enabled for draw batches!", system::ILogger::ELL_ERROR); + return false; + } + + using offset_t = SCachedCreationParameters::streaming_buffer_t::size_type; + constexpr offset_t MaxAlignment = sizeof(InstanceData); + // allocator initialization needs us to round up to PoT + const auto MaxPOTAlignment = hlsl::roundUpToPoT(MaxAlignment); + auto* streaming = m_cachedCreationParams.streamingBuffer.get(); + if (streaming->getAddressAllocator().max_alignment() < MaxPOTAlignment) + { + logger.log("Draw AABB Streaming Buffer cannot guarantee the alignments we require!"); + return false; + } + + auto* const streamingPtr = reinterpret_cast(streaming->getBufferPointer()); + assert(streamingPtr); + + auto& commandBuffer = params.commandBuffer; + commandBuffer->bindGraphicsPipeline(m_batchPipeline.get()); + commandBuffer->setLineWidth(params.lineWidth); + asset::SBufferBinding indexBinding = { .offset = 0, .buffer = m_indicesBuffer }; + commandBuffer->bindIndexBuffer(indexBinding, asset::EIT_32BIT); + + auto srcIt = aabbInstances.begin(); + auto setInstancesRange = [&](InstanceData* data, uint32_t count) -> void { + for (uint32_t i = 0; i < count; i++) + { + auto inst = data + i; + *inst = *srcIt; + inst->transform = hlsl::mul(params.cameraMat, inst->transform); + srcIt++; + + if (srcIt == aabbInstances.end()) + break; + } + }; + + const uint32_t numInstances = aabbInstances.size(); + uint32_t remainingInstancesBytes = numInstances * sizeof(InstanceData); + while (srcIt != aabbInstances.end()) + { + uint32_t blockByteSize = core::alignUp(remainingInstancesBytes, MaxAlignment); + bool allocated = false; + + offset_t blockOffset = SCachedCreationParameters::streaming_buffer_t::invalid_value; + const uint32_t smallestAlloc = hlsl::max(core::alignUp(sizeof(InstanceData), MaxAlignment), streaming->getAddressAllocator().min_size()); + while (blockByteSize >= smallestAlloc) + { + std::chrono::steady_clock::time_point waitTill = std::chrono::steady_clock::now() + std::chrono::milliseconds(1u); + if (streaming->multi_allocate(waitTill, 1, &blockOffset, &blockByteSize, &MaxAlignment) == 0u) + { + allocated = true; + break; + } + + streaming->cull_frees(); + blockByteSize >>= 1; + } + + if (!allocated) + { + logger.log("Failed to allocate a chunk from streaming buffer for the next drawcall batch.", system::ILogger::ELL_ERROR); + return false; + } + + const uint32_t instanceCount = blockByteSize / sizeof(InstanceData); + auto* const streamingInstancesPtr = reinterpret_cast(streamingPtr + blockOffset); + setInstancesRange(streamingInstancesPtr, instanceCount); + + if (streaming->needsManualFlushOrInvalidate()) + { + const video::ILogicalDevice::MappedMemoryRange flushRange(streaming->getBuffer()->getBoundMemory().memory, blockOffset, blockByteSize); + m_cachedCreationParams.utilities->getLogicalDevice()->flushMappedMemoryRanges(1, &flushRange); + } + + remainingInstancesBytes -= instanceCount * sizeof(InstanceData); + + SInstancedPC pc; + pc.pInstanceBuffer = m_cachedCreationParams.streamingBuffer->getBuffer()->getDeviceAddress() + blockOffset; + + commandBuffer->pushConstants(m_batchPipeline->getLayout(), asset::IShader::E_SHADER_STAGE::ESS_VERTEX, offsetof(ext::debug_draw::PushConstants, ipc), sizeof(SInstancedPC), &pc); + commandBuffer->drawIndexed(IndicesCount, instanceCount, 0, 0, 0); + + streaming->multi_deallocate(1, &blockOffset, &blockByteSize, waitInfo); + } + + return true; + } + + static inline hlsl::float32_t3x4 getTransformFromAABB(const hlsl::shapes::AABB<3, float>& aabb) + { + const auto diagonal = aabb.getExtent(); + hlsl::float32_t3x4 transform; + transform[0][3] = aabb.minVx.x; + transform[1][3] = aabb.minVx.y; + transform[2][3] = aabb.minVx.z; + transform[0][0] = diagonal.x; + transform[1][1] = diagonal.y; + transform[2][2] = diagonal.z; + return transform; + } + + protected: + struct ConstructorParams + { + SCachedCreationParameters creationParams; + core::smart_refctd_ptr singlePipeline = nullptr; + core::smart_refctd_ptr batchPipeline = nullptr; + core::smart_refctd_ptr indicesBuffer = nullptr; + }; + + DrawAABB(ConstructorParams&& params) : + m_cachedCreationParams(std::move(params.creationParams)), + m_singlePipeline(std::move(params.singlePipeline)), + m_batchPipeline(std::move(params.batchPipeline)), + m_indicesBuffer(std::move(params.indicesBuffer)) + {} + ~DrawAABB() override {} + + private: + static core::smart_refctd_ptr createPipeline(SCreationParameters& params, const video::IGPUPipelineLayout* pipelineLayout, const DrawMode mode); + static bool createStreamingBuffer(SCreationParameters& params); + static core::smart_refctd_ptr createIndicesBuffer(SCreationParameters& params); + + core::smart_refctd_ptr m_indicesBuffer; + + SCachedCreationParameters m_cachedCreationParams; + + core::smart_refctd_ptr m_singlePipeline; + core::smart_refctd_ptr m_batchPipeline; +}; +} + +#endif diff --git a/include/nbl/ext/DebugDraw/builtin/hlsl/common.hlsl b/include/nbl/ext/DebugDraw/builtin/hlsl/common.hlsl new file mode 100644 index 0000000000..b665c9d43a --- /dev/null +++ b/include/nbl/ext/DebugDraw/builtin/hlsl/common.hlsl @@ -0,0 +1,56 @@ +#ifndef _NBL_DEBUG_DRAW_EXT_COMMON_HLSL +#define _NBL_DEBUG_DRAW_EXT_COMMON_HLSL + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#ifdef __HLSL_VERSION +#include "nbl/builtin/hlsl/math/linalg/fast_affine.hlsl" +#include "nbl/builtin/hlsl/glsl_compat/core.hlsl" +#include "nbl/builtin/hlsl/bda/__ptr.hlsl" +#endif + +namespace nbl +{ +namespace ext +{ +namespace debug_draw +{ + +struct InstanceData +{ + hlsl::float32_t4x4 transform; + hlsl::float32_t4 color; +}; + +struct SSinglePC +{ + InstanceData instance; +}; + +struct SInstancedPC +{ + uint64_t pInstanceBuffer; +}; + +struct PushConstants +{ + SSinglePC spc; + SInstancedPC ipc; +}; + +#ifdef __HLSL_VERSION +struct PSInput +{ + float32_t4 position : SV_Position; + nointerpolation float32_t4 color : TEXCOORD0; +}; + +float32_t3 getUnitAABBVertex() +{ + return (hlsl::promote(hlsl::glsl::gl_VertexIndex()) >> uint32_t3(0,2,1)) & 0x1u; +} +#endif + +} +} +} +#endif diff --git a/include/nbl/ext/DebugDraw/builtin/hlsl/draw_aabb.unified.hlsl b/include/nbl/ext/DebugDraw/builtin/hlsl/draw_aabb.unified.hlsl new file mode 100644 index 0000000000..0b51f7de53 --- /dev/null +++ b/include/nbl/ext/DebugDraw/builtin/hlsl/draw_aabb.unified.hlsl @@ -0,0 +1,39 @@ +#include "nbl/ext/DebugDraw/builtin/hlsl/common.hlsl" + +using namespace nbl::hlsl; +using namespace nbl::ext::debug_draw; + +[[vk::push_constant]] PushConstants pc; + +[shader("vertex")] +PSInput aabb_vertex_single() +{ + PSInput output; + float32_t3 vertex = getUnitAABBVertex(); + + output.position = math::linalg::promoted_mul(pc.spc.instance.transform, vertex); + output.color = pc.spc.instance.color; + + return output; +} + +[shader("vertex")] +PSInput aabb_vertex_instances() +{ + PSInput output; + const float32_t3 vertex = getUnitAABBVertex(); + InstanceData instance = vk::BufferPointer(pc.ipc.pInstanceBuffer + sizeof(InstanceData) * glsl::gl_InstanceIndex()).Get(); + + output.position = math::linalg::promoted_mul(instance.transform, vertex); + output.color = instance.color; + + return output; +} + +[shader("pixel")] +float32_t4 aabb_fragment(PSInput input) : SV_TARGET +{ + float32_t4 outColor = input.color; + + return outColor; +} diff --git a/include/nbl/ext/FullScreenTriangle/FullScreenTriangle.h b/include/nbl/ext/FullScreenTriangle/FullScreenTriangle.h index 1abebf23ea..2046e5b592 100644 --- a/include/nbl/ext/FullScreenTriangle/FullScreenTriangle.h +++ b/include/nbl/ext/FullScreenTriangle/FullScreenTriangle.h @@ -10,79 +10,26 @@ namespace nbl::ext::FullScreenTriangle { struct ProtoPipeline final { - inline core::smart_refctd_ptr createDefaultVertexShader(asset::IAssetManager* assMan, video::ILogicalDevice* device, system::ILogger* logger=nullptr) - { - if (!assMan || !device) - return nullptr; - - using namespace ::nbl::asset; - IAssetLoader::SAssetLoadParams lp = {}; - lp.logger = logger; - lp.workingDirectory = ""; // virtual root - auto assetBundle = assMan->getAsset("nbl/builtin/hlsl/ext/FullScreenTriangle/default.vert.hlsl",lp); - const auto assets = assetBundle.getContents(); - if (assets.empty()) - return nullptr; - - auto source = IAsset::castDown(assets[0]); - if (!source) - return nullptr; - - return device->compileShader({ .source = source.get(), .stage = hlsl::ESS_VERTEX }); - } - public: - inline ProtoPipeline(asset::IAssetManager* assMan, video::ILogicalDevice* device, system::ILogger* logger=nullptr) - { - m_vxShader = createDefaultVertexShader(assMan,device,logger); - } + static core::smart_refctd_ptr createDefaultVertexShader(asset::IAssetManager* assMan, video::ILogicalDevice* device, system::ILogger* logger=nullptr); + static core::smart_refctd_ptr mount(core::smart_refctd_ptr logger, system::ISystem* system, video::ILogicalDevice* device, const std::string_view archiveAlias = "nbl/ext/FullScreenTriangle"); + + ProtoPipeline(asset::IAssetManager* assMan, video::ILogicalDevice* device, system::ILogger* logger=nullptr); - inline operator bool() const {return m_vxShader.get();} + operator bool() const; - inline core::smart_refctd_ptr createPipeline( + core::smart_refctd_ptr createPipeline( const video::IGPUPipelineBase::SShaderSpecInfo& fragShader, video::IGPUPipelineLayout* layout, - video::IGPURenderpass* renderpass, + const video::IGPURenderpass* renderpass, const uint32_t subpassIx=0, asset::SBlendParams blendParams = {}, + asset::SRasterizationParams rasterizationParams = DefaultRasterParams, const hlsl::SurfaceTransform::FLAG_BITS swapchainTransform=hlsl::SurfaceTransform::FLAG_BITS::IDENTITY_BIT - ) - { - if (!renderpass || !bool(*this) || hlsl::bitCount(swapchainTransform)!=1) - return nullptr; - - using namespace ::nbl::video; - auto device = const_cast(renderpass->getOriginDevice()); - - core::smart_refctd_ptr m_retval; - { - const auto orientationAsUint32 = static_cast(swapchainTransform); - - IGPUPipelineBase::SShaderEntryMap specConstants; - specConstants[0] = std::span{ reinterpret_cast(&orientationAsUint32), sizeof(orientationAsUint32)}; - - IGPUGraphicsPipeline::SCreationParams params[1]; - params[0].layout = layout; - params[0].vertexShader = { .shader = m_vxShader.get(), .entryPoint = "main", .entries = &specConstants }; - params[0].fragmentShader = fragShader; - params[0].cached = { - .vertexInput = {}, // The Full Screen Triangle doesn't use any HW vertex input state - .primitiveAssembly = {}, - .rasterization = DefaultRasterParams, - .blend = blendParams, - .subpassIx = subpassIx - }; - params[0].renderpass = renderpass; - - if (!device->createGraphicsPipelines(nullptr,params,&m_retval)) - return nullptr; - } - return m_retval; - } - + ); core::smart_refctd_ptr m_vxShader; - // The default is correct for us + constexpr static inline asset::SRasterizationParams DefaultRasterParams = { .faceCullingMode = asset::EFCM_NONE, .depthWriteEnable = false, @@ -90,17 +37,6 @@ struct ProtoPipeline final }; }; - -/* - Helper function for drawing full screen triangle. - It should be called between command buffer render pass - records. -*/ -static inline bool recordDrawCall(video::IGPUCommandBuffer* commandBuffer) -{ - constexpr auto VERTEX_COUNT = 3; - constexpr auto INSTANCE_COUNT = 1; - return commandBuffer->draw(VERTEX_COUNT,INSTANCE_COUNT,0,0); -} +bool recordDrawCall(video::IGPUCommandBuffer* commandBuffer); } -#endif \ No newline at end of file +#endif diff --git a/include/nbl/ext/ImGui/ImGui.h b/include/nbl/ext/ImGui/ImGui.h index 5f3c1d5f08..16f080d102 100644 --- a/include/nbl/ext/ImGui/ImGui.h +++ b/include/nbl/ext/ImGui/ImGui.h @@ -99,6 +99,14 @@ class UI final : public core::IReferenceCounted //! optional, no cache used if not provided core::smart_refctd_ptr pipelineCache = nullptr; + + struct PrecompiledShaders + { + core::smart_refctd_ptr vertex = nullptr, fragment = nullptr; + }; + + //! optional, precompiled spirv shaders (experimental) + std::optional spirv = std::nullopt; }; //! parameters which may change every frame, used with the .update call to interact with ImGuiIO; we require a very *required* minimum - if you need to cover more IO options simply get the IO with ImGui::GetIO() to customize them (they all have default values you can change before calling the .update) diff --git a/include/nbl/ext/ImGui/builtin/hlsl/fragment.hlsl b/include/nbl/ext/ImGui/builtin/hlsl/fragment.hlsl index 26e2b461a3..a0e70dfebd 100644 --- a/include/nbl/ext/ImGui/builtin/hlsl/fragment.hlsl +++ b/include/nbl/ext/ImGui/builtin/hlsl/fragment.hlsl @@ -22,13 +22,11 @@ #error "NBL_SAMPLERS_COUNT must be defined!" #endif -#include "common.hlsl" +#include "pc.hlsl" #include "psinput.hlsl" using namespace nbl::ext::imgui; -[[vk::push_constant]] struct PushConstants pc; - // separable image samplers to handle textures we do descriptor-index [[vk::binding(NBL_TEXTURES_BINDING_IX, NBL_TEXTURES_SET_IX)]] Texture2D textures[NBL_TEXTURES_COUNT]; [[vk::binding(NBL_SAMPLER_STATES_BINDING_IX, NBL_SAMPLER_STATES_SET_IX)]] SamplerState samplerStates[NBL_SAMPLERS_COUNT]; @@ -39,6 +37,7 @@ using namespace nbl::ext::imgui; to request per object data with BDA */ +[shader("pixel")] float4 PSMain(PSInput input) : SV_Target0 { // BDA for requesting object data diff --git a/include/nbl/ext/ImGui/builtin/hlsl/pc.hlsl b/include/nbl/ext/ImGui/builtin/hlsl/pc.hlsl new file mode 100644 index 0000000000..103ce5b6c1 --- /dev/null +++ b/include/nbl/ext/ImGui/builtin/hlsl/pc.hlsl @@ -0,0 +1,8 @@ +#ifndef _NBL_IMGUI_EXT_PC_HLSL_ +#define _NBL_IMGUI_EXT_PC_HLSL_ + +// TODO: have only unified.hlsl uber shader and common.hlsl then update imgui cpp files, doing a quick workaround for my prebuilds +#include "common.hlsl" +[[vk::push_constant]] struct nbl::ext::imgui::PushConstants pc; + +#endif // _NBL_IMGUI_EXT_PC_HLSL_ diff --git a/include/nbl/ext/ImGui/builtin/hlsl/vertex.hlsl b/include/nbl/ext/ImGui/builtin/hlsl/vertex.hlsl index 1651060c58..ef4582146e 100644 --- a/include/nbl/ext/ImGui/builtin/hlsl/vertex.hlsl +++ b/include/nbl/ext/ImGui/builtin/hlsl/vertex.hlsl @@ -1,10 +1,8 @@ -#include "common.hlsl" +#include "pc.hlsl" #include "psinput.hlsl" using namespace nbl::ext::imgui; -[[vk::push_constant]] struct PushConstants pc; - struct VSInput { [[vk::location(0)]] float2 position : POSITION; @@ -18,6 +16,7 @@ struct VSInput to request per object data with BDA */ +[shader("vertex")] PSInput VSMain(VSInput input, uint drawID : SV_InstanceID) { PSInput output; diff --git a/include/nbl/ext/MitsubaLoader/CElementBSDF.h b/include/nbl/ext/MitsubaLoader/CElementBSDF.h index 2b424e9a20..4729c36dc5 100644 --- a/include/nbl/ext/MitsubaLoader/CElementBSDF.h +++ b/include/nbl/ext/MitsubaLoader/CElementBSDF.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_EXT_MISTUBA_LOADER_C_ELEMENT_BSDF_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_BSDF_H_INCLUDED_ -#ifndef __C_ELEMENT_BSDF_H_INCLUDED__ -#define __C_ELEMENT_BSDF_H_INCLUDED__ #include "nbl/ext/MitsubaLoader/CElementTexture.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ +namespace nbl::ext::MitsubaLoader +{ class CElementBSDF : public IElement { @@ -33,6 +29,7 @@ class CElementBSDF : public IElement COATING, ROUGHCOATING, BUMPMAP, + NORMALMAP, PHONG, WARD, MIXTURE_BSDF, @@ -43,9 +40,39 @@ class CElementBSDF : public IElement //HANRAHAN_KRUEGER, //IRAWAN_MARSCHNER }; + static inline core::unordered_map compStringToTypeMap() + { + return { + {"diffuse", CElementBSDF::Type::DIFFUSE}, + {"roughdiffuse", CElementBSDF::Type::ROUGHDIFFUSE}, + {"dielectric", CElementBSDF::Type::DIELECTRIC}, + {"thindielectric", CElementBSDF::Type::THINDIELECTRIC}, + {"roughdielectric", CElementBSDF::Type::ROUGHDIELECTRIC}, + {"conductor", CElementBSDF::Type::CONDUCTOR}, + {"roughconductor", CElementBSDF::Type::ROUGHCONDUCTOR}, + {"plastic", CElementBSDF::Type::PLASTIC}, + {"roughplastic", CElementBSDF::Type::ROUGHPLASTIC}, + {"coating", CElementBSDF::Type::COATING}, + {"roughcoating", CElementBSDF::Type::ROUGHCOATING}, + {"bumpmap", CElementBSDF::Type::BUMPMAP}, + {"normalmap", CElementBSDF::Type::NORMALMAP}, + {"phong", CElementBSDF::Type::PHONG}, + {"ward", CElementBSDF::Type::WARD}, + {"mixturebsdf", CElementBSDF::Type::MIXTURE_BSDF}, + {"blendbsdf", CElementBSDF::Type::BLEND_BSDF}, + {"mask", CElementBSDF::Type::MASK}, + {"twosided", CElementBSDF::Type::TWO_SIDED}, + {"difftrans", CElementBSDF::Type::DIFFUSE_TRANSMITTER}//, + //{"hk", CElementBSDF::Type::HANRAHAN_KRUEGER}, + //{"irawan", CElementBSDF::Type::IRAWAN_MARSCHNER} + }; + } + struct DiffuseTransmitter { - DiffuseTransmitter() : transmittance(0.5f) {} + constexpr static inline Type VariantType = Type::DIFFUSE_TRANSMITTER; + + inline DiffuseTransmitter() : transmittance(0.5f) {} inline DiffuseTransmitter& operator=(const DiffuseTransmitter& other) { @@ -57,8 +84,8 @@ class CElementBSDF : public IElement }; struct AllDiffuse { - AllDiffuse() : reflectance(0.5f), alpha(0.2f), useFastApprox(false) {} - ~AllDiffuse() {} + inline AllDiffuse() : reflectance(0.5f), alpha(0.2f), useFastApprox(false) {} + inline ~AllDiffuse() {} inline AllDiffuse& operator=(const AllDiffuse& other) { @@ -76,6 +103,14 @@ class CElementBSDF : public IElement CElementTexture::FloatOrTexture alpha; // not the parameter from Oren-Nayar bool useFastApprox; }; + struct Diffuse : AllDiffuse + { + constexpr static inline Type VariantType = Type::DIFFUSE; + }; + struct RoughDiffuse : AllDiffuse + { + constexpr static inline Type VariantType = Type::ROUGHDIFFUSE; + }; struct RoughSpecularBase { enum NormalDistributionFunction : uint32_t @@ -86,13 +121,13 @@ class CElementBSDF : public IElement ASHIKHMIN_SHIRLEY }; - RoughSpecularBase(float defaultAlpha) : distribution(GGX), specularReflectance(1.f), + inline RoughSpecularBase(float defaultAlpha) : distribution(GGX), specularReflectance(1.f), // union ignores ctors, and ctors are important to not try to free garbage strings alphaU(core::nan()), alphaV(core::nan()) { alpha = defaultAlpha; } - virtual ~RoughSpecularBase() {} + inline ~RoughSpecularBase() {} inline RoughSpecularBase& operator=(const RoughSpecularBase& other) { @@ -123,12 +158,40 @@ class CElementBSDF : public IElement }; CElementTexture::SpectrumOrTexture specularReflectance; }; + struct AllConductor : RoughSpecularBase + { + inline AllConductor() : AllConductor("cu",nullptr) {} + inline AllConductor(const std::string_view material, system::logger_opt_ptr logger); + inline AllConductor(SPropertyElementData&& _eta, SPropertyElementData&& _k, system::logger_opt_ptr logger) : + RoughSpecularBase(0.1f), eta(_eta), k(_k), extEta(TransmissiveBase::findIOR("air",logger)) {} + + inline AllConductor& operator=(const AllConductor& other) + { + RoughSpecularBase::operator=(other); + eta = other.eta; + k = other.k; + extEta = other.extEta; + return *this; + } + + SPropertyElementData eta,k; + float extEta; + }; + struct Conductor : AllConductor + { + constexpr static inline Type VariantType = Type::CONDUCTOR; + }; + struct RoughConductor : AllConductor + { + constexpr static inline Type VariantType = Type::ROUGHCONDUCTOR; + }; struct TransmissiveBase { - static float findIOR(const std::string& name); + static float findIOR(const std::string_view name, system::logger_opt_ptr logger); - TransmissiveBase(float _intIOR, float _extIOR) : intIOR(_intIOR), extIOR(_extIOR), specularTransmittance(1.f) {} - TransmissiveBase(const std::string& _intIOR, const std::string& _extIOR) : TransmissiveBase(findIOR(_intIOR), findIOR(_extIOR)) {} + inline TransmissiveBase(float _intIOR, float _extIOR) : intIOR(_intIOR), extIOR(_extIOR), specularTransmittance(1.f) {} + inline TransmissiveBase(const std::string_view _intIOR, const std::string_view _extIOR, system::logger_opt_ptr logger) : + TransmissiveBase(findIOR(_intIOR,logger), findIOR(_extIOR,logger)) {} inline TransmissiveBase& operator=(const TransmissiveBase& other) { @@ -144,9 +207,10 @@ class CElementBSDF : public IElement }; struct AllDielectric : RoughSpecularBase, TransmissiveBase { - AllDielectric() : RoughSpecularBase(0.1f), TransmissiveBase("bk7","air") {} - AllDielectric(float intIOR, float extIOR) : RoughSpecularBase(0.1f), TransmissiveBase(intIOR,extIOR) {} - AllDielectric(const std::string& intIOR, const std::string& extIOR) : RoughSpecularBase(0.1f), TransmissiveBase(intIOR,extIOR) {} + inline AllDielectric() : RoughSpecularBase(0.1f), TransmissiveBase("bk7","air",nullptr) {} + inline AllDielectric(float intIOR, float extIOR) : RoughSpecularBase(0.1f), TransmissiveBase(intIOR,extIOR) {} + inline AllDielectric(const std::string_view intIOR, const std::string_view extIOR, system::logger_opt_ptr logger) : + RoughSpecularBase(0.1f), TransmissiveBase(intIOR,extIOR,logger) {} inline AllDielectric& operator=(const AllDielectric& other) { @@ -155,29 +219,25 @@ class CElementBSDF : public IElement return *this; } }; - struct AllConductor : RoughSpecularBase + struct Dielectric : AllDielectric { - AllConductor() : AllConductor("cu") {} - AllConductor(const std::string& material); - AllConductor(SPropertyElementData&& _eta, SPropertyElementData&& _k) : RoughSpecularBase(0.1f), eta(_eta), k(_k), extEta(TransmissiveBase::findIOR("air")) {} - - inline AllConductor& operator=(const AllConductor& other) - { - RoughSpecularBase::operator=(other); - eta = other.eta; - k = other.k; - extEta = other.extEta; - return *this; - } - - SPropertyElementData eta,k; - float extEta; + constexpr static inline Type VariantType = Type::DIELECTRIC; + }; + struct ThinDielectric : AllDielectric + { + constexpr static inline Type VariantType = Type::THINDIELECTRIC; + }; + struct RoughDielectric : AllDielectric + { + constexpr static inline Type VariantType = Type::ROUGHDIELECTRIC; }; struct AllPlastic : RoughSpecularBase, TransmissiveBase { - AllPlastic() : RoughSpecularBase(0.1f), TransmissiveBase("polypropylene", "air"), nonlinear(false) {} - AllPlastic(float intIOR, float extIOR) : RoughSpecularBase(0.1f), TransmissiveBase(intIOR, extIOR), nonlinear(false) {} - AllPlastic(const std::string& intIOR, const std::string& extIOR) : RoughSpecularBase(0.1f), TransmissiveBase(intIOR, extIOR), nonlinear(false) {} + inline AllPlastic() : RoughSpecularBase(0.1f), TransmissiveBase("polypropylene","air",nullptr), nonlinear(false) {} + inline AllPlastic(float intIOR, float extIOR) : + RoughSpecularBase(0.1f), TransmissiveBase(intIOR,extIOR), nonlinear(false) {} + inline AllPlastic(const std::string_view intIOR, const std::string_view extIOR, system::logger_opt_ptr logger) : + RoughSpecularBase(0.1f), TransmissiveBase(intIOR,extIOR,logger), nonlinear(false) {} inline AllPlastic& operator=(const AllPlastic& other) { @@ -190,13 +250,21 @@ class CElementBSDF : public IElement bool nonlinear; CElementTexture::SpectrumOrTexture diffuseReflectance = 0.5f; + }; + struct Plastic : AllPlastic + { + constexpr static inline Type VariantType = Type::PLASTIC; + }; + struct RoughPlastic : AllPlastic + { + constexpr static inline Type VariantType = Type::ROUGHPLASTIC; };/* struct HanrahanKrueger { class CPhaseElement { }; - HanrahanKrueger(const std::string& material); + HanrahanKrueger(const std::string_view material); HanrahanKrueger() : HanrahanKrueger("skin1") {} bool tNOTs = false; @@ -218,15 +286,16 @@ class CElementBSDF : public IElement };*/ struct MetaBSDF { - _NBL_STATIC_INLINE_CONSTEXPR size_t MaxChildCount = 32u; + constexpr static inline size_t MaxChildCount = 32u; size_t childCount = 0u; CElementBSDF* bsdf[MaxChildCount] = { nullptr }; }; struct AllCoating : MetaBSDF, RoughSpecularBase, TransmissiveBase { - _NBL_STATIC_INLINE_CONSTEXPR size_t MaxChildCount = 1u; + constexpr static inline size_t MaxChildCount = 1u; - AllCoating() : MetaBSDF(), RoughSpecularBase(0.1f), TransmissiveBase("bk7","air"), thickness(1.f), sigmaA(0.f) {} + inline AllCoating() final : MetaBSDF(), RoughSpecularBase(0.1f), + TransmissiveBase("bk7","air",nullptr), thickness(1.f), sigmaA(0.f) {} inline AllCoating& operator=(const AllCoating& other) { @@ -241,45 +310,69 @@ class CElementBSDF : public IElement float thickness; CElementTexture::SpectrumOrTexture sigmaA; }; - struct BumpMap : MetaBSDF + struct Coating final : AllCoating + { + constexpr static inline Type VariantType = Type::COATING; + }; + struct RoughCoating final : AllCoating + { + constexpr static inline Type VariantType = Type::ROUGHCOATING; + }; + struct BumpMap final : MetaBSDF { - CElementTexture* texture; - bool wasNormal; + constexpr static inline Type VariantType = Type::BUMPMAP; + + CElementTexture* texture = nullptr; }; - struct MixtureBSDF : MetaBSDF + struct NormalMap final : MetaBSDF { + constexpr static inline Type VariantType = Type::NORMALMAP; + + CElementTexture* texture = nullptr; + }; + struct MixtureBSDF final : MetaBSDF + { + constexpr static inline Type VariantType = Type::MIXTURE_BSDF; + uint32_t weightCount = 0u; float weights[MetaBSDF::MaxChildCount] = { 1.f }; }; - struct BlendBSDF : MetaBSDF + struct BlendBSDF final : MetaBSDF { - _NBL_STATIC_INLINE_CONSTEXPR size_t MaxChildCount = 2u; + constexpr static inline Type VariantType = Type::BLEND_BSDF; + constexpr static inline size_t MaxChildCount = 2u; - BlendBSDF() : weight(0.5f) {} + inline BlendBSDF() : weight(0.5f) {} CElementTexture::SpectrumOrTexture weight; }; - struct Mask : MetaBSDF + struct Mask final : MetaBSDF { - _NBL_STATIC_INLINE_CONSTEXPR size_t MaxChildCount = 1u; + constexpr static inline Type VariantType = Type::MASK; + constexpr static inline size_t MaxChildCount = 1u; - Mask() : opacity(0.5f) {} + inline Mask() : opacity(0.5f) {} CElementTexture::SpectrumOrTexture opacity; }; - struct TwoSided : MetaBSDF + struct TwoSided final : MetaBSDF { - _NBL_STATIC_INLINE_CONSTEXPR size_t MaxChildCount = 1u; + constexpr static inline Type VariantType = Type::TWO_SIDED; + constexpr static inline size_t MaxChildCount = 1u; }; // legacy and evil struct Phong { + constexpr static inline Type VariantType = Type::PHONG; + CElementTexture::FloatOrTexture exponent = 30.f; CElementTexture::SpectrumOrTexture specularReflectance = 0.2f; CElementTexture::SpectrumOrTexture diffuseReflectance = 0.5f; }; struct Ward { + constexpr static inline Type VariantType = Type::WARD; + enum Type : uint32_t { WARD, @@ -292,7 +385,36 @@ class CElementBSDF : public IElement CElementTexture::SpectrumOrTexture specularReflectance = 0.2f; CElementTexture::SpectrumOrTexture diffuseReflectance = 0.5f; }; + + // + using variant_list_t = core::type_list< + Diffuse, + RoughDiffuse, + Dielectric, + ThinDielectric, + RoughDielectric, + Conductor, + RoughConductor, + Plastic, + RoughPlastic, + Coating, + RoughCoating, + BumpMap, + NormalMap, + Phong, + Ward, + MixtureBSDF, + BlendBSDF, + Mask, + TwoSided, + DiffuseTransmitter/*, + HanrahanKrueger, + IrawanMarschner*/ + >; + // + static AddPropertyMap compAddPropertyMap(); + // inline CElementBSDF(const char* id) : IElement(id), type(Type::INVALID) { } @@ -304,96 +426,116 @@ class CElementBSDF : public IElement { } - inline CElementBSDF& operator=(const CElementBSDF& other) + template + inline void visit(Visitor&& func) { - IElement::operator=(other); - type = other.type; switch (type) { case CElementBSDF::Type::DIFFUSE: [[fallthrough]]; case CElementBSDF::Type::ROUGHDIFFUSE: - diffuse = other.diffuse; + func(diffuse); break; case CElementBSDF::Type::DIELECTRIC: [[fallthrough]]; case CElementBSDF::Type::THINDIELECTRIC: [[fallthrough]]; case CElementBSDF::Type::ROUGHDIELECTRIC: - dielectric = other.dielectric; + func(dielectric); break; case CElementBSDF::Type::CONDUCTOR: [[fallthrough]]; case CElementBSDF::Type::ROUGHCONDUCTOR: - conductor = other.conductor; + func(conductor); break; case CElementBSDF::Type::PLASTIC: [[fallthrough]]; case CElementBSDF::Type::ROUGHPLASTIC: - plastic = other.plastic; + func(plastic); break; case CElementBSDF::Type::COATING: [[fallthrough]]; case CElementBSDF::Type::ROUGHCOATING: - coating = other.coating; + func(coating); break; case CElementBSDF::Type::BUMPMAP: - bumpmap = other.bumpmap; + func(bumpmap); + break; + case CElementBSDF::Type::NORMALMAP: + func(normalmap); break; case CElementBSDF::Type::PHONG: - phong = other.phong; + func(phong); break; case CElementBSDF::Type::WARD: - ward = other.ward; + func(ward); break; case CElementBSDF::Type::MIXTURE_BSDF: - mixturebsdf = other.mixturebsdf; + func(mixturebsdf); break; case CElementBSDF::Type::BLEND_BSDF: - blendbsdf = other.blendbsdf; + func(blendbsdf); break; case CElementBSDF::Type::MASK: - mask = other.mask; + func(mask); break; case CElementBSDF::Type::TWO_SIDED: - twosided = other.twosided; + func(twosided); break; case CElementBSDF::Type::DIFFUSE_TRANSMITTER: - difftrans = other.difftrans; + func(difftrans); break; //case CElementBSDF::Type::HANRAHAN_KRUEGER: - //hk = HanrahanKrueger(); + //func(hk); //break; //case CElementBSDF::Type::IRAWAN_MARSCHNER: - //irawan = IrawanMarschner(); + //func(irwan); //break; default: break; } + } + template + inline void visit(Visitor&& visitor) const + { + const_cast(this)->visit([&](T& var)->void + { + visitor(const_cast(var)); + } + ); + } + + inline CElementBSDF& operator=(const CElementBSDF& other) + { + IElement::operator=(other); + type = other.type; + IElement::copyVariant(this,&other); return *this; } - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override; - IElement::Type getType() const override { return IElement::Type::BSDF; } + bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override; + + constexpr static inline auto ElementType = IElement::Type::BSDF; + inline IElement::Type getType() const override { return ElementType; } std::string getLogName() const override { return "bsdf"; } - bool processChildData(IElement* _child, const std::string& name) override; + bool processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) override; - bool isMeta() const + inline bool isMeta() const { switch (type) { - case COATING: [[fallthrough]]; - case ROUGHCOATING: [[fallthrough]]; - case TWO_SIDED: [[fallthrough]]; - case MASK: [[fallthrough]]; - case BLEND_BSDF: [[fallthrough]]; - case MIXTURE_BSDF: [[fallthrough]]; - case BUMPMAP: - return true; - default: - return false; + case COATING: [[fallthrough]]; + case ROUGHCOATING: [[fallthrough]]; + case TWO_SIDED: [[fallthrough]]; + case MASK: [[fallthrough]]; + case BLEND_BSDF: [[fallthrough]]; + case MIXTURE_BSDF: [[fallthrough]]; + case BUMPMAP: [[fallthrough]]; + case NORMALMAP: + return true; + default: + return false; } } @@ -408,6 +550,7 @@ class CElementBSDF : public IElement AllPlastic plastic; AllCoating coating; BumpMap bumpmap; + NormalMap normalmap; Phong phong; Ward ward; MixtureBSDF mixturebsdf; @@ -422,9 +565,5 @@ class CElementBSDF : public IElement }; - -} } -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementEmissionProfile.h b/include/nbl/ext/MitsubaLoader/CElementEmissionProfile.h index 972cf3915e..84927497df 100644 --- a/include/nbl/ext/MitsubaLoader/CElementEmissionProfile.h +++ b/include/nbl/ext/MitsubaLoader/CElementEmissionProfile.h @@ -1,77 +1,60 @@ // 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_EXT_MISTUBA_LOADER_C_ELEMENT_EMISSION_PROFILE_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_EMISSION_PROFILE_H_INCLUDED_ -#ifndef __C_ELEMENT_EMISSION_PROFILE_H_INCLUDED__ -#define __C_ELEMENT_EMISSION_PROFILE_H_INCLUDED__ -#include "vectorSIMD.h" #include "nbl/ext/MitsubaLoader/CElementTexture.h" #include "nbl/ext/MitsubaLoader/CElementTransform.h" -namespace nbl +namespace nbl::ext::MitsubaLoader { -namespace ext -{ -namespace MitsubaLoader + +struct CElementEmissionProfile final : public IElement { + constexpr static inline uint16_t MaxPathLen = 1024u; -struct CElementEmissionProfile : public IElement { + static AddPropertyMap compAddPropertyMap(); - CElementEmissionProfile(const char* id) : IElement(id), normalization(EN_NONE), flatten(0.0) /*no blending by default*/ {} - CElementEmissionProfile() : IElement(""), normalization(EN_NONE) {} - CElementEmissionProfile(const CElementEmissionProfile& other) : IElement("") + inline CElementEmissionProfile(const char* id) : IElement(id), normalization(EN_NONE), flatten(0.0) /*no blending by default*/ {} + inline CElementEmissionProfile() : IElement(""), normalization(EN_NONE) {} + inline CElementEmissionProfile(const CElementEmissionProfile& other) : IElement("") { operator=(other); } - CElementEmissionProfile(CElementEmissionProfile&& other) : IElement("") - { - operator=(std::move(other)); - } + inline ~CElementEmissionProfile() {} inline CElementEmissionProfile& operator=(const CElementEmissionProfile& other) { IElement::operator=(other); - filename = other.filename; + memcpy(filename,other.filename,MaxPathLen); return *this; } - inline CElementEmissionProfile& operator=(CElementEmissionProfile&& other) - { - IElement::operator=(std::move(other)); - std::swap(filename, other.filename); - return *this; - } - virtual ~CElementEmissionProfile() - { - } + inline bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override {return true;} + bool processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) override; - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override { - return true; - } - bool processChildData(IElement* _child, const std::string& name) override; - IElement::Type getType() const override { return IElement::Type::EMISSION_PROFILE; } - std::string getLogName() const override { return "emissionprofile "; } + constexpr static inline auto ElementType = IElement::Type::EMISSION_PROFILE; + inline IElement::Type getType() const override { return ElementType; } + inline std::string getLogName() const override { return "emissionprofile "; } - enum E_NORMALIZE + enum E_NORMALIZE : uint8_t { EN_UNIT_MAX, //! normalize the intensity by dividing out the maximum intensity - EN_UNIT_AVERAGE_OVER_IMPLIED_DOMAIN, //! normlize by energy - integrate the profile over the hemisphere as well as the solid angles where the profile has emission above 0. + EN_UNIT_AVERAGE_OVER_IMPLIED_DOMAIN, //! normalize by energy - integrate the profile over the hemisphere as well as the solid angles where the profile has emission above 0. EN_UNIT_AVERAGE_OVER_FULL_DOMAIN, //! similar to UNIT_AVERAGE_OVER_IMPLIED_DOMAIN but in this case we presume the soild angle of the domain is (CIESProfile::vAngles.front()-CIESProfile::vAngles.back())*4.f EN_NONE //! no normalization }; - std::string filename; + char filename[MaxPathLen]; E_NORMALIZE normalization; + // how much we flatten the profile towards a uniform distribution float flatten; }; } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementEmitter.h b/include/nbl/ext/MitsubaLoader/CElementEmitter.h index 87afdc860d..8eaebcae74 100644 --- a/include/nbl/ext/MitsubaLoader/CElementEmitter.h +++ b/include/nbl/ext/MitsubaLoader/CElementEmitter.h @@ -1,24 +1,19 @@ -// 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_MISTUBA_LOADER_C_ELEMENT_EMITTER_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_EMITTER_H_INCLUDED_ -#ifndef __C_ELEMENT_EMITTER_H_INCLUDED__ -#define __C_ELEMENT_EMITTER_H_INCLUDED__ -#include - -#include "vectorSIMD.h" #include "nbl/ext/MitsubaLoader/CElementTexture.h" #include "nbl/ext/MitsubaLoader/CElementEmissionProfile.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader +#include + + +namespace nbl::ext::MitsubaLoader { - class CElementEmitter : public IElement { public: @@ -27,7 +22,7 @@ class CElementEmitter : public IElement INVALID, POINT, AREA, - SPOT, + //SPOT, // deprecated, use POINT with an IES profile instead! DIRECTIONAL, COLLIMATED, //SKY, @@ -36,43 +31,67 @@ class CElementEmitter : public IElement ENVMAP, CONSTANT }; + static inline core::unordered_map compStringToTypeMap() + { + return { + {"point", CElementEmitter::Type::POINT}, + {"area", CElementEmitter::Type::AREA}, + {"directional", CElementEmitter::Type::DIRECTIONAL}, + {"collimated", CElementEmitter::Type::COLLIMATED},/* + {"sky", CElementEmitter::Type::SKY}, + {"sun", CElementEmitter::Type::SUN}, + {"sunsky", CElementEmitter::Type::SUNSKY},*/ + {"envmap", CElementEmitter::Type::ENVMAP}, + {"constant", CElementEmitter::Type::CONSTANT} + }; + } struct SampledEmitter { - SampledEmitter() : samplingWeight(1.f) {} + inline SampledEmitter() : samplingWeight(1.f) {} float samplingWeight; }; - struct Point : SampledEmitter - { - core::vectorSIMDf intensity = core::vectorSIMDf(1.f); // Watts Steradian^-1 - }; - struct Area : SampledEmitter + struct DeltaDistributionEmitter : SampledEmitter + { + // Watts + hlsl::float32_t3 intensity = {1.f,1.f,1.f}; + }; + struct SolidAngleEmitter : SampledEmitter + { + // Watts Steradian^-1 + hlsl::float32_t3 radiance = {1.f,1.f,1.f}; + }; + struct EmissionProfileEmitter + { + CElementEmissionProfile* emissionProfile = nullptr; + }; + struct Point : DeltaDistributionEmitter, EmissionProfileEmitter { - core::vectorSIMDf radiance = core::vectorSIMDf(1.f); // Watts Meter^-2 Steradian^-1 - CElementEmissionProfile* emissionProfile = nullptr; + constexpr static inline Type VariantType = Type::POINT; }; - struct Spot : SampledEmitter + struct Area : SolidAngleEmitter, EmissionProfileEmitter { - core::vectorSIMDf intensity = core::vectorSIMDf(1.f); // Watts Steradian^-1 - float cutoffAngle = 20.f; // degrees, its the cone half-angle - float beamWidth = NAN; - CElementTexture* texture = nullptr; + constexpr static inline Type VariantType = Type::AREA; }; struct Directional : SampledEmitter { - core::vectorSIMDf irradiance = core::vectorSIMDf(1.f); // Watts Meter^-2 + constexpr static inline Type VariantType = Type::DIRECTIONAL; + + hlsl::float32_t3 irradiance = {1.f,1.f,1.f}; // Watts Meter^-2 }; struct Collimated : SampledEmitter { - core::vectorSIMDf power = core::vectorSIMDf(1.f); // Watts + constexpr static inline Type VariantType = Type::COLLIMATED; + + hlsl::float32_t3 power = {1.f,1.f,1.f}; // Watts };/* struct Sky : SampledEmitter { float turbidity = 3.f; - core::vectorSIMDf albedo = core::vectorSIMDf(0.15f); - core::vectorSIMDf sunDirection = calculate default from tokyo japan at 15:00 on 10.07.2010; + hlsl::float32_t3 albedo = {0.15f,0.15f,0.15f}; + hlsl::float32_t3 sunDirection = calculate default from tokyo japan at 15:00 on 10.07.2010; float stretch = 1.f; // must be [1,2] int32_t resolution = 512; float scale = 1.f; @@ -80,7 +99,7 @@ class CElementEmitter : public IElement struct Sun : SampledEmitter { float turbidity = 3.f; - core::vectorSIMDf sunDirection = calculate default from tokyo japan at 15:00 on 10.07.2010; + hlsl::float32_t3 sunDirection = calculate default from tokyo japan at 15:00 on 10.07.2010; int32_t resolution = 512; float scale = 1.f; float sunRadiusScale = 1.f; @@ -92,190 +111,109 @@ class CElementEmitter : public IElement };*/ struct EnvMap : SampledEmitter { - SPropertyElementData filename; - float scale = 1.f; - float gamma = NAN; + constexpr static inline Type VariantType = Type::ENVMAP; + constexpr static inline uint16_t MaxPathLen = 1024u; + + char filename[MaxPathLen]; + float scale = 1.f; + float gamma = NAN; //bool cache = false; }; - struct Constant : SampledEmitter + struct Constant : SolidAngleEmitter { - core::vectorSIMDf radiance = core::vectorSIMDf(1.f); // Watts Meter^-2 Steradian^-1 + constexpr static inline Type VariantType = Type::CONSTANT; }; + // + using variant_list_t = core::type_list< + Point, + Area, + Directional, + Collimated, +// Sky, +// Sun, +// SunSky, + EnvMap, + Constant + >; + // + static AddPropertyMap compAddPropertyMap(); - CElementEmitter(const char* id) : IElement(id), type(Type::INVALID), /*toWorldType(IElement::Type::TRANSFORM),*/ transform() + // + inline CElementEmitter(const char* id) : IElement(id), type(Type::INVALID), /*toWorldType(IElement::Type::TRANSFORM),*/ transform() { } - CElementEmitter() : CElementEmitter("") {} - CElementEmitter(const CElementEmitter& other) : IElement(""), transform() + inline CElementEmitter() : CElementEmitter("") {} + inline CElementEmitter(const CElementEmitter& other) : IElement(""), transform() { operator=(other); } - CElementEmitter(CElementEmitter&& other) : IElement(""), transform() - { - operator=(std::move(other)); - } virtual ~CElementEmitter() { } - inline CElementEmitter& operator=(const CElementEmitter& other) + template + inline void visit(Visitor&& func) { - IElement::operator=(other); - transform = other.transform; - type = other.type; switch (type) { case Type::POINT: - point = other.point; + func(point); break; case Type::AREA: - area = other.area; - break; - case Type::SPOT: - spot = other.spot; + func(area); break; case Type::DIRECTIONAL: - directional = other.directional; + func(directional); break; case Type::COLLIMATED: - collimated = other.collimated; + func(collimated); break;/* case Type::SKY: - sky = other.sky; + func(sky); break; case Type::SUN: - sun = other.sun; + func(sun); break; case Type::SUNSKY: - sunsky = other.sunsky; + func(sunsky); break;*/ case Type::ENVMAP: - envmap = other.envmap; + func(envmap); break; case Type::CONSTANT: - constant = other.constant; + func(constant); break; default: break; } - return *this; + } + template + inline void visit(Visitor&& visitor) const + { + const_cast(this)->visit([&](T& var)->void + { + visitor(const_cast(var)); + } + ); } - inline CElementEmitter& operator=(CElementEmitter&& other) + inline CElementEmitter& operator=(const CElementEmitter& other) { - IElement::operator=(std::move(other)); - std::swap(transform,other.transform); - std::swap(type,other.type); - switch (type) - { - case Type::POINT: - std::swap(point,other.point); - break; - case Type::AREA: - std::swap(area,other.area); - break; - case Type::SPOT: - std::swap(spot,other.spot); - break; - case Type::DIRECTIONAL: - std::swap(directional,other.directional); - break; - case Type::COLLIMATED: - std::swap(collimated,other.collimated); - break;/* - case Type::SKY: - sky,other.sky; - break; - case Type::SUN: - sun,other.sun; - break; - case Type::SUNSKY: - sunsky,other.sunsky; - break;*/ - case Type::ENVMAP: - std::swap(envmap,other.envmap); - break; - case Type::CONSTANT: - std::swap(constant,other.constant); - break; - default: - break; - } + IElement::operator=(other); + transform = other.transform; + type = other.type; + IElement::copyVariant(this,&other); return *this; } - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override; - IElement::Type getType() const override { return IElement::Type::EMITTER; } + bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override; + + constexpr static inline auto ElementType = IElement::Type::EMITTER; + inline IElement::Type getType() const override { return ElementType; } std::string getLogName() const override { return "emitter"; } - bool processChildData(IElement* _child, const std::string& name) override - { - if (!_child) - return true; - switch (_child->getType()) - { - case IElement::Type::TRANSFORM: - { - auto tform = static_cast(_child); - if (name!="toWorld") - return false; - //toWorldType = IElement::Type::TRANSFORM; - switch (type) - { - case Type::POINT: - [[fallthrough]]; - case Type::SPOT: - [[fallthrough]]; - case Type::DIRECTIONAL: - [[fallthrough]]; - case Type::COLLIMATED: - [[fallthrough]]; - case Type::AREA: - [[fallthrough]]; - /* - case Type::SKY: - [[fallthrough]]; - case Type::SUN: - [[fallthrough]]; - case Type::SUNSKY: - [[fallthrough]];*/ - case Type::ENVMAP: - transform = *tform; - return true; - default: - break; - } - return false; - } - break;/* - case IElement::Type::ANIMATION: - auto anim = static_cast(_child); - if (anim->name!="toWorld") - return false; - toWorlType = IElement::Type::ANIMATION; - animation = anim; - return true; - break;*/ - case IElement::Type::EMISSION_PROFILE: { - if (type == Type::AREA) { - area.emissionProfile = static_cast(_child); - return true; - } - return false; - } - case IElement::Type::TEXTURE: - if (type!=SPOT || name!="texture") - return false; - spot.texture = static_cast(_child); - return true; - break; - default: - break; - } - return false; - } + bool processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) override; // Type type; @@ -291,7 +229,6 @@ class CElementEmitter : public IElement { Point point; Area area; - Spot spot; Directional directional; Collimated collimated;/* Sky sky; @@ -302,10 +239,5 @@ class CElementEmitter : public IElement }; }; - - -} } -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementFactory.h b/include/nbl/ext/MitsubaLoader/CElementFactory.h deleted file mode 100644 index 7543504b1d..0000000000 --- a/include/nbl/ext/MitsubaLoader/CElementFactory.h +++ /dev/null @@ -1,41 +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 __I_ELEMENT_FACTORY_H_INCLUDED__ -#define __I_ELEMENT_FACTORY_H_INCLUDED__ - -#include "nbl/ext/MitsubaLoader/CElementSensor.h" -#include "nbl/ext/MitsubaLoader/CElementIntegrator.h" -#include "nbl/ext/MitsubaLoader/CElementShape.h" - -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ - -class ParserManager; - -class CElementFactory -{ - public: - using return_type = std::pair; - using element_creation_func = return_type(*)(const char**, ParserManager*); - const static core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> createElementTable; - - //constructs certain elements based on element's name and its attributes - template - static return_type createElement(const char** _atts, ParserManager* _util); - // - static return_type processAlias(const char** _atts, ParserManager* _util); - static return_type processRef(const char** _atts, ParserManager* _util); -}; - - -} -} -} - -#endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementFilm.h b/include/nbl/ext/MitsubaLoader/CElementFilm.h index afe929180c..3da02a21b8 100644 --- a/include/nbl/ext/MitsubaLoader/CElementFilm.h +++ b/include/nbl/ext/MitsubaLoader/CElementFilm.h @@ -1,26 +1,20 @@ // 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_EXT_MISTUBA_LOADER_C_ELEMENT_FILM_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_FILM_H_INCLUDED_ -#ifndef __C_ELEMENT_FILM_H_INCLUDED__ -#define __C_ELEMENT_FILM_H_INCLUDED__ - -#include "nbl/macros.h" #include "nbl/ext/MitsubaLoader/CElementRFilter.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ +namespace nbl::ext::MitsubaLoader +{ -class CElementFilm : public IElement +class CElementFilm final : public IElement { public: - enum Type + enum Type : uint8_t { INVALID, HDR_FILM, @@ -28,7 +22,8 @@ class CElementFilm : public IElement LDR_FILM, MFILM }; - enum PixelFormat + // + enum PixelFormat : uint8_t { LUMINANCE, LUMINANCE_ALPHA, @@ -39,7 +34,7 @@ class CElementFilm : public IElement SPECTRUM, SPECTRUM_ALPHA }; - enum FileFormat + enum FileFormat : uint8_t { OPENEXR, RGBE, @@ -50,7 +45,7 @@ class CElementFilm : public IElement MATHEMATICA, NUMPY }; - enum ComponentFormat + enum ComponentFormat : uint8_t { FLOAT16, FLOAT32, @@ -58,10 +53,20 @@ class CElementFilm : public IElement }; struct HDR { + constexpr static inline Type VariantType = Type::HDR_FILM; + bool attachLog = true; }; + struct TiledHDR : HDR + { + constexpr static inline Type VariantType = Type::TILED_HDR; + + // TODO: sure we don't have more members? + }; struct LDR { + constexpr static inline Type VariantType = Type::LDR_FILM; + enum TonemapMethod { GAMMA, @@ -75,7 +80,9 @@ class CElementFilm : public IElement }; struct M { - M() : digits(4) + constexpr static inline Type VariantType = Type::MFILM; + + inline M() : digits(4) { variable[0] = 'd'; variable[1] = 'a'; @@ -84,35 +91,107 @@ class CElementFilm : public IElement variable[4] = 0; } int32_t digits; - _NBL_STATIC_INLINE_CONSTEXPR size_t MaxVarNameLen = 63; // matlab + constexpr static inline size_t MaxVarNameLen = 63; // matlab char variable[MaxVarNameLen+1]; }; - CElementFilm(const char* id) : IElement(id), type(Type::HDR_FILM), + // + using variant_list_t = core::type_list< + HDR, + TiledHDR, + LDR, + M + >; + static inline core::unordered_map compStringToTypeMap() + { + return { + {"hdrfilm", Type::HDR_FILM}, + {"tiledhdrfilm",Type::TILED_HDR}, + {"ldrfilm", Type::LDR_FILM}, + {"mfilm", Type::MFILM} + }; + } + static AddPropertyMap compAddPropertyMap(); + + inline CElementFilm(const char* id) : IElement(id), type(Type::HDR_FILM), width(768), height(576), cropOffsetX(0), cropOffsetY(0), cropWidth(INT_MAX), cropHeight(INT_MAX), fileFormat(OPENEXR), pixelFormat(RGB), componentFormat(FLOAT16), banner(true), highQualityEdges(false), rfilter("") { hdrfilm = HDR(); } - virtual ~CElementFilm() + virtual inline ~CElementFilm() { } - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override; - IElement::Type getType() const override { return IElement::Type::FILM; } - std::string getLogName() const override { return "film"; } + inline void initialize() + { + switch (type) + { + case CElementFilm::Type::LDR_FILM: + fileFormat = CElementFilm::FileFormat::PNG; + //componentFormat = UINT8; + ldrfilm = CElementFilm::LDR(); + break; + case CElementFilm::Type::MFILM: + width = 1; + height = 1; + fileFormat = CElementFilm::FileFormat::MATLAB; + pixelFormat = CElementFilm::PixelFormat::LUMINANCE; + mfilm = CElementFilm::M(); + break; + default: + break; + } + } - inline bool processChildData(IElement* _child, const std::string& name) override + template + inline void visit(Visitor&& visitor) + { + switch (type) + { + case CElementFilm::Type::LDR_FILM: + visitor(ldrfilm); + break; + case CElementFilm::Type::MFILM: + visitor(mfilm); + break; + default: + visitor(hdrfilm); + break; + } + } + template + inline void visit(Visitor&& visitor) const + { + const_cast(this)->visit([&](T& var)->void + { + visitor(const_cast(var)); + } + ); + } + + bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override; + + constexpr static inline auto ElementType = IElement::Type::FILM; + inline IElement::Type getType() const override { return ElementType; } + inline std::string getLogName() const override { return "film"; } + + inline bool processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) override { if (!_child) return true; if (_child->getType() != IElement::Type::RFILTER) + { + logger.log("CElementFilm only expects type %d children, is %d instead",system::ILogger::ELL_ERROR,IElement::Type::RFILTER,_child->getType()); return false; + } auto _rfilter = static_cast(_child); if (_rfilter->type == CElementRFilter::Type::INVALID) + { + logger.log("CElementRFilter::Type::INVALID used as child in CElementFilm",system::ILogger::ELL_ERROR); return false; + } rfilter = *_rfilter; return true; } @@ -121,7 +200,7 @@ class CElementFilm : public IElement Type type; int32_t width,height; int32_t cropOffsetX,cropOffsetY,cropWidth,cropHeight; - FileFormat fileFormat; + FileFormat fileFormat = OPENEXR; PixelFormat pixelFormat; ComponentFormat componentFormat; bool banner; @@ -129,12 +208,12 @@ class CElementFilm : public IElement CElementRFilter rfilter; union { - HDR hdrfilm; - LDR ldrfilm; - M mfilm; + HDR hdrfilm; + LDR ldrfilm; + M mfilm; }; - _NBL_STATIC_INLINE_CONSTEXPR size_t MaxPathLen = 256; + constexpr static inline size_t MaxPathLen = 256; char outputFilePath[MaxPathLen+1] = {0}; char denoiserBloomFilePath[MaxPathLen+1] = {0}; int32_t cascadeCount = 1; @@ -142,14 +221,11 @@ class CElementFilm : public IElement float cascadeLuminanceStart = core::nan(); float denoiserBloomScale = 0.0f; float denoiserBloomIntensity = 0.0f; - _NBL_STATIC_INLINE_CONSTEXPR size_t MaxTonemapperArgsLen = 128; + constexpr static inline size_t MaxTonemapperArgsLen = 128; char denoiserTonemapperArgs[MaxTonemapperArgsLen+1] = {0}; float envmapRegularizationFactor = 0.5f; // 1.0f means based envmap luminance, 0.0f means uniform }; } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementIntegrator.h b/include/nbl/ext/MitsubaLoader/CElementIntegrator.h index 4a171f717b..7ed5f0cbb4 100644 --- a/include/nbl/ext/MitsubaLoader/CElementIntegrator.h +++ b/include/nbl/ext/MitsubaLoader/CElementIntegrator.h @@ -1,26 +1,22 @@ // 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_EXT_MISTUBA_LOADER_C_ELEMENT_INTEGRATOR_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_INTEGRATOR_H_INCLUDED_ -#ifndef __C_ELEMENT_INTEGRATOR_H_INCLUDED__ -#define __C_ELEMENT_INTEGRATOR_H_INCLUDED__ #include "nbl/ext/MitsubaLoader/IElement.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader + +namespace nbl::ext::MitsubaLoader { -class CElementIntegrator : public IElement +class CElementIntegrator final : public IElement { public: - enum Type + enum Type : uint8_t { - INVALID, AO, DIRECT, PATH, @@ -38,10 +34,37 @@ class CElementIntegrator : public IElement VPL, IRR_CACHE, MULTI_CHANNEL, - FIELD_EXTRACT + FIELD_EXTRACT, + INVALID }; - struct AmbientOcclusion + static inline core::unordered_map compStringToTypeMap() + { + return { + {"ao", Type::AO}, + {"direct", Type::DIRECT}, + {"path", Type::PATH}, + {"volpath_simple", Type::VOL_PATH_SIMPLE}, + {"volpath", Type::VOL_PATH}, + {"bdpt", Type::BDPT}, + {"photonmapper", Type::PHOTONMAPPER}, + {"ppm", Type::PPM}, + {"sppm", Type::SPPM}, + {"pssmlt", Type::PSSMLT}, + {"mlt", Type::MLT}, + {"erpt", Type::ERPT}, + {"ptracer", Type::ADJ_P_TRACER}, + {"adaptive", Type::ADAPTIVE}, + {"vpl", Type::VPL}, + {"irrcache", Type::IRR_CACHE}, + {"multichannel", Type::MULTI_CHANNEL}, + {"field", Type::FIELD_EXTRACT} + }; + } + + struct AmbientOcclusion final { + constexpr static inline Type VariantType = Type::AO; + int32_t shadingSamples = 1; float rayLength = -1.f; }; @@ -50,10 +73,12 @@ class CElementIntegrator : public IElement bool hideEmitters = false; bool hideEnvironment = false; }; - struct DirectIllumination : EmitterHideableBase + struct DirectIllumination final : EmitterHideableBase { - int32_t emitterSamples = 0xdeadbeefu; - int32_t bsdfSamples = 0xdeadbeefu; + constexpr static inline Type VariantType = Type::DIRECT; + + int32_t emitterSamples = static_cast(0xdeadbeefu); + int32_t bsdfSamples = static_cast(0xdeadbeefu); bool strictNormals = false; }; struct MonteCarloTracingBase @@ -65,14 +90,22 @@ class CElementIntegrator : public IElement { bool strictNormals = false; }; + struct UniDirectionalPathTracing final : PathTracing + { + constexpr static inline Type VariantType = Type::PATH; + }; struct SimpleVolumetricPathTracing : PathTracing { + constexpr static inline Type VariantType = Type::VOL_PATH_SIMPLE; }; struct ExtendedVolumetricPathTracing : SimpleVolumetricPathTracing { + constexpr static inline Type VariantType = Type::VOL_PATH; }; - struct BiDirectionalPathTracing : MonteCarloTracingBase + struct BiDirectionalPathTracing final : PathTracing { + constexpr static inline Type VariantType = Type::BDPT; + bool lightImage = true; bool sampleDirect = true; }; @@ -80,26 +113,31 @@ class CElementIntegrator : public IElement { int32_t granularity = 0; }; - struct PhotonMapping : PhotonMappingBase, EmitterHideableBase + struct PhotonMapping final : PhotonMappingBase, EmitterHideableBase { + constexpr static inline Type VariantType = Type::PHOTONMAPPER; + int32_t directSamples = 16; int32_t glossySamples = 32; int32_t globalPhotons = 250000; int32_t causticPhotons = 250000; int32_t volumePhotons = 250000; - float globalLURadius = 0.05; - float causticLURadius = 0.0125; - int32_t LUSize = 120; + float globalLookupRadius = 0.05; + float causticLookupRadius = 0.0125; + int32_t lookupSize = 120; }; struct ProgressivePhotonMapping : PhotonMappingBase { + constexpr static inline Type VariantType = Type::PPM; + int32_t photonCount = 250000; float initialRadius = 0.f; float alpha = 0.7f; int32_t maxPasses = -1; }; - struct StochasticProgressivePhotonMapping : ProgressivePhotonMapping + struct StochasticProgressivePhotonMapping final : ProgressivePhotonMapping { + constexpr static inline Type VariantType = Type::SPPM; }; struct MetropolisLightTransportBase : MonteCarloTracingBase { @@ -107,8 +145,10 @@ class CElementIntegrator : public IElement int32_t luminanceSamples = 100000; bool twoStage = false; }; - struct PrimarySampleSpaceMetropolisLightTransport : MetropolisLightTransportBase + struct PrimarySampleSpaceMetropolisLightTransport final : MetropolisLightTransportBase { + constexpr static inline Type VariantType = Type::PSSMLT; + bool bidirectional = true; float pLarge = 0.3f; }; @@ -120,30 +160,40 @@ class CElementIntegrator : public IElement bool manifoldPerturbation = false; float lambda = 50.f; }; - struct PathSpaceMetropolisLightTransport : MetropolisLightTransportBase, PerturbateableBase + struct PathSpaceMetropolisLightTransport final : MetropolisLightTransportBase, PerturbateableBase { + constexpr static inline Type VariantType = Type::MLT; + bool bidirectionalMutation = true; }; - struct EnergyRedistributionPathTracing : MonteCarloTracingBase, PerturbateableBase + struct EnergyRedistributionPathTracing final : MonteCarloTracingBase, PerturbateableBase { + constexpr static inline Type VariantType = Type::ERPT; + float numChains = 1.f; float maxChains = 0.f; int32_t chainLength = 1; int32_t directSamples = 16; }; - struct AdjointParticleTracing : MonteCarloTracingBase + struct AdjointParticleTracing final : MonteCarloTracingBase { + constexpr static inline Type VariantType = Type::ADJ_P_TRACER; + uint32_t granularity = 200000; bool bruteForce = false; }; - struct VirtualPointLights + struct VirtualPointLights final { + constexpr static inline Type VariantType = Type::VPL; + int32_t maxPathDepth = 5; int32_t shadowMap = 512; float clamping = 0.1f; }; struct FieldExtraction { + constexpr static inline Type VariantType = Type::FIELD_EXTRACT; + enum Type { INVALID, @@ -164,6 +214,7 @@ class CElementIntegrator : public IElement } Type field; + // TODO: Specral properties really need a redo SPropertyElementData undefined; }; struct MetaIntegrator @@ -172,14 +223,18 @@ class CElementIntegrator : public IElement size_t childCount = 0u; CElementIntegrator* children[maxChildCount] = { nullptr }; }; - struct AdaptiveIntegrator : MetaIntegrator + struct AdaptiveIntegrator final : MetaIntegrator { + constexpr static inline Type VariantType = Type::ADAPTIVE; + float maxError = 0.05f; float pValue = 0.05f; int32_t maxSampleFactor = 32; }; - struct IrradianceCacheIntegrator : MetaIntegrator + struct IrradianceCacheIntegrator final : MetaIntegrator { + constexpr static inline Type VariantType = Type::IRR_CACHE; + int32_t resolution = 14; float quality = 1.f; bool gradients = true; @@ -190,89 +245,131 @@ class CElementIntegrator : public IElement bool indirectOnly = false; bool debug = false; }; - struct MultiChannelIntegrator : MetaIntegrator + struct MultiChannelIntegrator final : MetaIntegrator { + constexpr static inline Type VariantType = Type::MULTI_CHANNEL; }; - CElementIntegrator(const char* id) : IElement(id), type(Type::INVALID) + // + using variant_list_t = core::type_list< + AmbientOcclusion, + DirectIllumination, + UniDirectionalPathTracing, + SimpleVolumetricPathTracing, + ExtendedVolumetricPathTracing, + BiDirectionalPathTracing, + PhotonMapping, + ProgressivePhotonMapping, + StochasticProgressivePhotonMapping, + PrimarySampleSpaceMetropolisLightTransport, + PathSpaceMetropolisLightTransport, + EnergyRedistributionPathTracing, + AdjointParticleTracing, + AdaptiveIntegrator, + VirtualPointLights, + IrradianceCacheIntegrator, + MultiChannelIntegrator, + FieldExtraction + >; + // + static AddPropertyMap compAddPropertyMap(); + + // + inline CElementIntegrator(const char* id) : IElement(id), type(Type::INVALID) { } - virtual ~CElementIntegrator() + inline ~CElementIntegrator() { } - inline CElementIntegrator& operator=(const CElementIntegrator& other) + template + inline void visit(Visitor&& visitor) { - IElement::operator=(other); - type = other.type; switch (type) { case CElementIntegrator::Type::AO: - ao = other.ao; + visitor(ao); break; case CElementIntegrator::Type::DIRECT: - direct = other.direct; + visitor(direct); break; case CElementIntegrator::Type::PATH: - path = other.path; + visitor(path); break; case CElementIntegrator::Type::VOL_PATH_SIMPLE: - volpath_simple = other.volpath_simple; + visitor(volpath_simple); break; case CElementIntegrator::Type::VOL_PATH: - volpath = other.volpath; + visitor(volpath); break; case CElementIntegrator::Type::BDPT: - bdpt = other.bdpt; + visitor(bdpt); break; case CElementIntegrator::Type::PHOTONMAPPER: - photonmapper = other.photonmapper; + visitor(photonmapper); break; case CElementIntegrator::Type::PPM: - ppm = other.ppm; + visitor(ppm); break; case CElementIntegrator::Type::SPPM: - sppm = other.sppm; + visitor(sppm); break; case CElementIntegrator::Type::PSSMLT: - pssmlt = other.pssmlt; + visitor(pssmlt); break; case CElementIntegrator::Type::MLT: - mlt = other.mlt; + visitor(mlt); break; case CElementIntegrator::Type::ERPT: - erpt = other.erpt; + visitor(erpt); break; case CElementIntegrator::Type::ADJ_P_TRACER: - ptracer = other.ptracer; + visitor(ptracer); break; case CElementIntegrator::Type::ADAPTIVE: - adaptive = other.adaptive; + visitor(adaptive); break; case CElementIntegrator::Type::VPL: - vpl = other.vpl; + visitor(vpl); break; case CElementIntegrator::Type::IRR_CACHE: - irrcache = other.irrcache; + visitor(irrcache); break; case CElementIntegrator::Type::MULTI_CHANNEL: - multichannel = other.multichannel; + visitor(multichannel); break; case CElementIntegrator::Type::FIELD_EXTRACT: - field = other.field; + visitor(field); break; default: break; } + } + template + inline void visit(Visitor&& visitor) const + { + const_cast(this)->visit([&](T& var)->void + { + visitor(const_cast(var)); + } + ); + } + + inline CElementIntegrator& operator=(const CElementIntegrator& other) + { + IElement::operator=(other); + type = other.type; + IElement::copyVariant(this,&other); return *this; } - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override; - IElement::Type getType() const override { return IElement::Type::INTEGRATOR; } - std::string getLogName() const override { return "integrator"; } + bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override; + + constexpr static inline auto ElementType = IElement::Type::INTEGRATOR; + inline IElement::Type getType() const override { return ElementType; } + inline std::string getLogName() const override { return "integrator"; } - bool processChildData(IElement* _child, const std::string& name) override + inline bool processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) override { if (!_child) return true; @@ -282,11 +379,12 @@ class CElementIntegrator : public IElement case Type::IRR_CACHE: [[fallthrough]]; case Type::MULTI_CHANNEL: - if (_child->getType() != IElement::Type::INTEGRATOR) - return false; - break; + if (_child->getType() == IElement::Type::INTEGRATOR) + break; + [[fallthrough]]; default: - break; + logger.log("Only IrradianceCache or MultiChannel can only have another nested inside", system::ILogger::ELL_ERROR); + return false; } switch (type) { @@ -302,7 +400,8 @@ class CElementIntegrator : public IElement return true; } break; - default: + default: // to make compiler shut up + assert(false); break; } return false; @@ -314,9 +413,9 @@ class CElementIntegrator : public IElement { AmbientOcclusion ao; DirectIllumination direct; + PathTracing path; SimpleVolumetricPathTracing volpath_simple; ExtendedVolumetricPathTracing volpath; - PathTracing path; BiDirectionalPathTracing bdpt; PhotonMapping photonmapper; ProgressivePhotonMapping ppm; @@ -334,9 +433,5 @@ class CElementIntegrator : public IElement }; - } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementRFilter.h b/include/nbl/ext/MitsubaLoader/CElementRFilter.h index aa2da6dee3..4d4be06424 100644 --- a/include/nbl/ext/MitsubaLoader/CElementRFilter.h +++ b/include/nbl/ext/MitsubaLoader/CElementRFilter.h @@ -1,26 +1,22 @@ -// 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_MISTUBA_LOADER_C_ELEMENT_R_FILTER_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_R_FILTER_H_INCLUDED_ -#ifndef __C_ELEMENT_R_FILTER_H_INCLUDED__ -#define __C_ELEMENT_R_FILTER_H_INCLUDED__ #include "nbl/ext/MitsubaLoader/PropertyElement.h" - #include "nbl/ext/MitsubaLoader/IElement.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader + +namespace nbl::ext::MitsubaLoader { -class CElementRFilter : public IElement +class CElementRFilter final : public IElement { public: - enum Type + enum Type : uint8_t { INVALID, BOX, @@ -30,30 +26,99 @@ class CElementRFilter : public IElement CATMULLROM, LANCZOS }; + struct Gaussian { + constexpr static inline Type VariantType = Type::GAUSSIAN; + float sigma = NAN; // can't look at mitsuba source to figure out the default it uses }; struct MitchellNetravali { + constexpr static inline Type VariantType = Type::MITCHELL; + + float B = 1.f / 3.f; + float C = 1.f / 3.f; + }; + struct CatmullRom + { + constexpr static inline Type VariantType = Type::CATMULLROM; + float B = 1.f / 3.f; float C = 1.f / 3.f; }; struct LanczosSinc { + constexpr static inline Type VariantType = Type::LANCZOS; + int32_t lobes = 3; }; - CElementRFilter(const char* id) : IElement(id), type(GAUSSIAN) + using variant_list_t = core::type_list< + Gaussian, + MitchellNetravali, + CatmullRom, + LanczosSinc + >; + static inline core::unordered_map compStringToTypeMap() + { + return { + std::make_pair("box", Type::BOX), + std::make_pair("tent", Type::TENT), + std::make_pair("gaussian", Type::GAUSSIAN), + std::make_pair("mitchell", Type::MITCHELL), + std::make_pair("catmullrom", Type::CATMULLROM), + std::make_pair("lanczos", Type::LANCZOS) + }; + } + static AddPropertyMap compAddPropertyMap(); + + inline CElementRFilter(const char* id) : IElement(id), type(GAUSSIAN) { gaussian = Gaussian(); } - virtual ~CElementRFilter() {} + inline ~CElementRFilter() {} + + template + inline void visit(Visitor&& visitor) + { + switch (type) + { + case Type::BOX: + [[fallthrough]]; + case Type::TENT: + break; + case Type::GAUSSIAN: + visitor(gaussian); + break; + case Type::MITCHELL: + visitor(mitchell); + break; + case Type::CATMULLROM: + visitor(catmullrom); + break; + case Type::LANCZOS: + visitor(lanczos); + break; + default: + break; + } + } + template + inline void visit(Visitor&& visitor) const + { + const_cast(this)->visit([&](T& var)->void + { + visitor(const_cast(var)); + } + ); + } + + bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override; - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override; - IElement::Type getType() const override { return IElement::Type::RFILTER; } - std::string getLogName() const override { return "rfilter"; } + constexpr static inline auto ElementType = IElement::Type::RFILTER; + inline IElement::Type getType() const override { return ElementType; } + inline std::string getLogName() const override { return "rfilter"; } // make these public Type type; @@ -61,7 +126,7 @@ class CElementRFilter : public IElement { Gaussian gaussian; MitchellNetravali mitchell; - MitchellNetravali catmullrom; + CatmullRom catmullrom; LanczosSinc lanczos; }; float kappa = 0.f; @@ -70,7 +135,4 @@ class CElementRFilter : public IElement } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementSampler.h b/include/nbl/ext/MitsubaLoader/CElementSampler.h index 621623770d..82f586c19e 100644 --- a/include/nbl/ext/MitsubaLoader/CElementSampler.h +++ b/include/nbl/ext/MitsubaLoader/CElementSampler.h @@ -1,56 +1,85 @@ // 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_EXT_MISTUBA_LOADER_C_ELEMENT_SAMPLER_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_SAMPLER_H_INCLUDED_ -#ifndef __C_ELEMENT_SAMPLER_H_INCLUDED__ -#define __C_ELEMENT_SAMPLER_H_INCLUDED__ #include "nbl/ext/MitsubaLoader/IElement.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ - -class CGlobalMitsubaMetadata; +namespace nbl::ext::MitsubaLoader +{ class CElementSampler : public IElement { public: - enum Type + enum Type : uint8_t { - INVALID, INDEPENDENT, STRATIFIED, LDSAMPLER, HALTON, HAMMERSLEY, - SOBOL + SOBOL, + INVALID }; + static inline core::unordered_map compStringToTypeMap() + { + return { + {"independent", Type::INDEPENDENT}, + {"stratified", Type::STRATIFIED}, + {"ldsampler", Type::LDSAMPLER}, + {"halton", Type::HALTON}, + {"hammersley", Type::HAMMERSLEY}, + {"sobol", Type::SOBOL} + }; + } + static AddPropertyMap compAddPropertyMap(); - CElementSampler(const char* id) : IElement(id), type(INVALID), sampleCount(4) {} - virtual ~CElementSampler() {} + inline CElementSampler(const char* id) : IElement(id), type(INVALID), sampleCount(4) {} + inline ~CElementSampler() {} - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override; - IElement::Type getType() const override { return IElement::Type::SAMPLER; } - std::string getLogName() const override { return "sampler"; } + inline void initialize() + { + sampleCount = 4; + switch (type) + { + case CElementSampler::Type::STRATIFIED: + [[fallthrough]]; + case CElementSampler::Type::LDSAMPLER: + dimension = 4; + break; + case CElementSampler::Type::HALTON: + [[fallthrough]]; + case CElementSampler::Type::HAMMERSLEY: + scramble = -1; + break; + case CElementSampler::Type::SOBOL: + scramble = 0; + break; + default: + break; + } + } + + bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override; + + constexpr static inline auto ElementType = IElement::Type::SAMPLER; + inline IElement::Type getType() const override { return ElementType; } + inline std::string getLogName() const override { return "sampler"; } // make these public + // TODO: these should be bitfields of a uint64_t, or pack into 8 bytes somehow Type type; int32_t sampleCount; union { int32_t dimension; + // TODO: document scramble seed? int32_t scramble; }; }; } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementSensor.h b/include/nbl/ext/MitsubaLoader/CElementSensor.h index c8214dd64c..d240830902 100644 --- a/include/nbl/ext/MitsubaLoader/CElementSensor.h +++ b/include/nbl/ext/MitsubaLoader/CElementSensor.h @@ -1,9 +1,9 @@ // 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_EXT_MISTUBA_LOADER_C_ELEMENT_SENSOR_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_SENSOR_H_INCLUDED_ -#ifndef __C_ELEMENT_SENSOR_H_INCLUDED__ -#define __C_ELEMENT_SENSOR_H_INCLUDED__ #include "nbl/ext/MitsubaLoader/IElement.h" #include "nbl/ext/MitsubaLoader/CElementTransform.h" @@ -11,20 +11,14 @@ #include "nbl/ext/MitsubaLoader/CElementSampler.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader +namespace nbl::ext::MitsubaLoader { - -class CElementSensor : public IElement +class CElementSensor final : public IElement { public: - enum Type + enum Type : uint8_t { - INVALID, PERSPECTIVE, THINLENS, ORTHOGRAPHIC, @@ -33,14 +27,16 @@ class CElementSensor : public IElement IRRADIANCEMETER, RADIANCEMETER, FLUENCEMETER, - PERSPECTIVE_RDIST + PERSPECTIVE_RDIST, + INVALID }; + constexpr static inline uint8_t MaxClipPlanes = 6u; struct ShutterSensor { - core::vectorSIMDf up = core::vectorSIMDf(0,1,0); - core::vectorSIMDf clipPlanes[MaxClipPlanes] = {}; + hlsl::float32_t3 up = hlsl::float32_t3(0,1,0); + hlsl::float32_t4 clipPlanes[MaxClipPlanes] = {}; float moveSpeed = core::nan(); float zoomSpeed = core::nan(); float rotateSpeed = core::nan(); @@ -54,6 +50,8 @@ class CElementSensor : public IElement }; struct PerspectivePinhole : CameraBase { + constexpr static inline Type VariantType = Type::PERSPECTIVE; + enum class FOVAxis { INVALID, @@ -76,6 +74,7 @@ class CElementSensor : public IElement }; struct Orthographic : CameraBase { + constexpr static inline Type VariantType = Type::ORTHOGRAPHIC; }; struct DepthOfFieldBase { @@ -84,83 +83,131 @@ class CElementSensor : public IElement }; struct PerspectiveThinLens : PerspectivePinhole, DepthOfFieldBase { + constexpr static inline Type VariantType = Type::THINLENS; }; struct TelecentricLens : Orthographic, DepthOfFieldBase { + constexpr static inline Type VariantType = Type::TELECENTRIC; }; struct SphericalCamera : CameraBase { + constexpr static inline Type VariantType = Type::SPHERICAL; }; struct IrradianceMeter : ShutterSensor { + constexpr static inline Type VariantType = Type::IRRADIANCEMETER; }; struct RadianceMeter : ShutterSensor { + constexpr static inline Type VariantType = Type::RADIANCEMETER; }; struct FluenceMeter : ShutterSensor { + constexpr static inline Type VariantType = Type::FLUENCEMETER; };/* struct PerspectivePinholeRadialDistortion : PerspectivePinhole { kc; };*/ - CElementSensor(const char* id) : IElement(id), type(Type::INVALID), /*toWorldType(IElement::Type::TRANSFORM),*/ transform(), film(""), sampler("") + using variant_list_t = core::type_list< + PerspectivePinhole, + PerspectiveThinLens, + Orthographic, + TelecentricLens, + SphericalCamera, + IrradianceMeter, + RadianceMeter, + FluenceMeter + >; + static inline core::unordered_map compStringToTypeMap() + { + return { + {"perspective", Type::PERSPECTIVE}, + {"thinlens", Type::THINLENS}, + {"orthographic", Type::ORTHOGRAPHIC}, + {"telecentric", Type::TELECENTRIC}, + {"spherical", Type::SPHERICAL}, + {"irradiancemeter", Type::IRRADIANCEMETER}, + {"radiancemeter", Type::RADIANCEMETER}, + {"fluencemeter", Type::FLUENCEMETER}/*, + {"perspective_rdist", PERSPECTIVE_RDIST}*/ + }; + } + static AddPropertyMap compAddPropertyMap(); + + inline CElementSensor(const char* id) : IElement(id), type(Type::INVALID), /*toWorldType(IElement::Type::TRANSFORM),*/ transform(), film(""), sampler("") { } - CElementSensor(const CElementSensor& other) : IElement(""), transform(), film(""), sampler("") + inline CElementSensor(const CElementSensor& other) : IElement(""), transform(), film(""), sampler("") { operator=(other); } - virtual ~CElementSensor() + inline ~CElementSensor() { } - inline CElementSensor& operator=(const CElementSensor& other) + template + inline void visit(Visitor&& visitor) { - IElement::operator=(other); - type = other.type; - transform = other.transform; switch (type) { case CElementSensor::Type::PERSPECTIVE: - perspective = other.perspective; + visitor(perspective); break; case CElementSensor::Type::THINLENS: - thinlens = other.thinlens; + visitor(thinlens); break; case CElementSensor::Type::ORTHOGRAPHIC: - orthographic = other.orthographic; + visitor(orthographic); break; case CElementSensor::Type::TELECENTRIC: - telecentric = other.telecentric; + visitor(telecentric); break; case CElementSensor::Type::SPHERICAL: - spherical = other.spherical; + visitor(spherical); break; case CElementSensor::Type::IRRADIANCEMETER: - irradiancemeter = other.irradiancemeter; + visitor(irradiancemeter); break; case CElementSensor::Type::RADIANCEMETER: - radiancemeter = other.radiancemeter; + visitor(radiancemeter); break; case CElementSensor::Type::FLUENCEMETER: - fluencemeter = other.fluencemeter; + visitor(fluencemeter); break; default: break; } + } + template + inline void visit(Visitor&& visitor) const + { + const_cast(this)->visit([&](T& var)->void + { + visitor(const_cast(var)); + } + ); + } + + inline CElementSensor& operator=(const CElementSensor& other) + { + IElement::operator=(other); + type = other.type; + transform = other.transform; + IElement::copyVariant(this,&other); film = other.film; sampler = other.sampler; return *this; } - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override; - IElement::Type getType() const override { return IElement::Type::SENSOR; } - std::string getLogName() const override { return "sensor"; } + bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override; + + constexpr static inline auto ElementType = IElement::Type::SENSOR; + inline IElement::Type getType() const override { return ElementType; } + inline std::string getLogName() const override { return "sensor"; } - bool processChildData(IElement* _child, const std::string& name) override + inline bool processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) override { if (!_child) return true; @@ -169,8 +216,11 @@ class CElementSensor : public IElement case IElement::Type::TRANSFORM: { auto tform = static_cast(_child); - if (name!="toWorld") + if (name != "toWorld") + { + logger.log("The nested inside needs to be named \"toWorld\"",system::ILogger::ELL_ERROR); return false; + } //toWorldType = IElement::Type::TRANSFORM; transform = *tform; return true; @@ -186,15 +236,18 @@ class CElementSensor : public IElement break;*/ case IElement::Type::FILM: film = *static_cast(_child); - if (film.type != CElementFilm::Type::INVALID) + if (film.type!=CElementFilm::Type::INVALID) return true; break; case IElement::Type::SAMPLER: sampler = *static_cast(_child); - if (sampler.type != CElementSampler::Type::INVALID) + if (sampler.type!=CElementSampler::Type::INVALID) return true; break; + default: + break; } + logger.log("Only valid nested children inside are: VALID , , and . The is not supported yet.",system::ILogger::ELL_ERROR); return false; } @@ -210,6 +263,7 @@ class CElementSensor : public IElement };*/ union { + CameraBase base; PerspectivePinhole perspective; PerspectiveThinLens thinlens; Orthographic orthographic; @@ -226,7 +280,4 @@ class CElementSensor : public IElement } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementShape.h b/include/nbl/ext/MitsubaLoader/CElementShape.h index 205023afea..b641c964d2 100644 --- a/include/nbl/ext/MitsubaLoader/CElementShape.h +++ b/include/nbl/ext/MitsubaLoader/CElementShape.h @@ -1,28 +1,26 @@ -// 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_MISTUBA_LOADER_C_ELEMENT_SHAPE_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_SHAPE_H_INCLUDED_ -#ifndef __C_ELEMENT_SHAPE_H_INCLUDED__ -#define __C_ELEMENT_SHAPE_H_INCLUDED__ #include "nbl/ext/MitsubaLoader/IElement.h" #include "nbl/ext/MitsubaLoader/CElementTransform.h" #include "nbl/ext/MitsubaLoader/CElementBSDF.h" #include "nbl/ext/MitsubaLoader/CElementEmitter.h" +#include "nbl/builtin/hlsl/math/linalg/basic.hlsl" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader + +namespace nbl::ext::MitsubaLoader { -class CElementShape : public IElement +class CElementShape final : public IElement { public: - enum Type + enum Type : uint8_t { INVALID, CUBE, @@ -38,51 +36,88 @@ class CElementShape : public IElement //HAIR, //HEIGHTFIELD }; + static inline core::unordered_map compStringToTypeMap() + { + return { + {"cube", CElementShape::Type::CUBE}, + {"sphere", CElementShape::Type::SPHERE}, + {"cylinder", CElementShape::Type::CYLINDER}, + {"rectangle", CElementShape::Type::RECTANGLE}, + {"disk", CElementShape::Type::DISK}, + {"obj", CElementShape::Type::OBJ}, + {"ply", CElementShape::Type::PLY}, + {"serialized", CElementShape::Type::SERIALIZED}, + {"shapegroup", CElementShape::Type::SHAPEGROUP}, + {"instance", CElementShape::Type::INSTANCE}/*, + {"hair", CElementShape::Type::HAIR}, + {"heightfield", CElementShape::Type::HEIGHTFIELD}*/ + }; + } + struct Base { bool flipNormals = false; }; + struct Cube : Base + { + constexpr static inline Type VariantType = Type::CUBE; + }; struct Sphere : Base { - core::vectorSIMDf center = core::vectorSIMDf(0,0,0); + constexpr static inline Type VariantType = Type::SPHERE; + + hlsl::float32_t3 center = {0,0,0}; float radius = 1.f; }; struct Cylinder : Base { - core::vectorSIMDf p0 = core::vectorSIMDf(0,0,0); - core::vectorSIMDf p1 = core::vectorSIMDf(0,0,1); + constexpr static inline Type VariantType = Type::CYLINDER; + + hlsl::float32_t3 p0 = {0,0,0}; + hlsl::float32_t3 p1 = {0,0,1}; float radius = 1.f; }; struct LoadedFromFileBase : Base { - SPropertyElementData filename; + constexpr static inline uint16_t MaxPathLen = 1024u; + + char filename[MaxPathLen]; //! Use face normals (any per-vertex normals will be discarded) - bool faceNormals = false; - float maxSmoothAngle = NAN; + bool faceNormals = false; + float maxSmoothAngle = NAN; }; struct Obj : LoadedFromFileBase { + constexpr static inline Type VariantType = Type::OBJ; + bool flipTexCoords = true; bool collapse = false; }; struct Ply : LoadedFromFileBase { - bool flipNormals = false; + constexpr static inline Type VariantType = Type::PLY; + bool srgb = true; }; struct Serialized : LoadedFromFileBase { + constexpr static inline Type VariantType = Type::SERIALIZED; + int32_t shapeIndex; - bool flipNormals; }; + // geometries basically struct ShapeGroup { - _NBL_STATIC_INLINE_CONSTEXPR size_t MaxChildCount = 128u; + constexpr static inline Type VariantType = Type::SHAPEGROUP; + constexpr static inline size_t MaxChildCount = 128u; + size_t childCount = 0u; CElementShape* children[MaxChildCount] = { nullptr }; }; struct Instance { + constexpr static inline Type VariantType = Type::INSTANCE; + CElementShape* parent = nullptr; };/* struct Hair : Base @@ -92,147 +127,125 @@ class CElementShape : public IElement float angleThreshold = 1.f; float reduction = 0.f; }; - struct HeightField + struct HeightField : Base { SPropertyElementData filename; boolean shadingNormals; - boolean flipNormals; int32_t width; int32_t height; float scale; CElementTexture* texture; };*/ - CElementShape(const char* id) : IElement(id), type(Type::INVALID), /*toWorldType(IElement::Type::TRANSFORM),*/ transform(), bsdf(nullptr), emitter(nullptr) + // + using variant_list_t = core::type_list< + Sphere, + Cylinder, + Obj, + Ply, + Serialized, + ShapeGroup, + Instance + >; + // + static AddPropertyMap compAddPropertyMap(); + + inline CElementShape(const char* id) : IElement(id), type(Type::INVALID), /*toWorldType(IElement::Type::TRANSFORM),*/ transform(), bsdf(nullptr), emitter(nullptr) { } - CElementShape(const CElementShape& other) : IElement(""), transform(), bsdf(nullptr), emitter(nullptr) + inline CElementShape(const CElementShape& other) : IElement(""), transform(), bsdf(nullptr), emitter(nullptr) { operator=(other); } - CElementShape(CElementShape&& other) : IElement(""), transform(), bsdf(nullptr), emitter(nullptr) + inline CElementShape(CElementShape&& other) : IElement(""), transform(), bsdf(nullptr), emitter(nullptr) { operator=(std::move(other)); } - virtual ~CElementShape() + inline ~CElementShape() { } - inline CElementShape& operator=(const CElementShape& other) + template + inline void visit(Visitor&& visitor) { - IElement::operator=(other); - transform = other.transform; - type = other.type; switch (type) { - case Type::CUBE: - cube = other.cube; + case CElementShape::Type::CUBE: + visitor(cube); break; - case Type::SPHERE: - sphere = other.sphere; + case CElementShape::Type::SPHERE: + visitor(sphere); break; - case Type::CYLINDER: - cylinder = other.cylinder; + case CElementShape::Type::CYLINDER: + visitor(cylinder); break; - case Type::RECTANGLE: - rectangle = other.rectangle; + case CElementShape::Type::RECTANGLE: + visitor(rectangle); break; - case Type::DISK: - disk = other.disk; + case CElementShape::Type::DISK: + visitor(disk); break; - case Type::OBJ: - obj = other.obj; + case CElementShape::Type::OBJ: + visitor(obj); break; - case Type::PLY: - ply = other.ply; + case CElementShape::Type::PLY: + visitor(ply); break; - case Type::SERIALIZED: - serialized = other.serialized; + case CElementShape::Type::SERIALIZED: + visitor(serialized); break; - case Type::SHAPEGROUP: - shapegroup = other.shapegroup; + case CElementShape::Type::SHAPEGROUP: + visitor(shapegroup); break; - case Type::INSTANCE: - instance = other.instance; + case CElementShape::Type::INSTANCE: + visitor(instance); break;/* - case Type::HAIR: - hair = other.hair; + case CElementShape::Type::HAIR: + visitor(hair); break; - case Type::HEIGHTFIELD: - heightfield = other.heightfield; + case CElementShape::Type::HEIGHTFIELD: + visitor(heightfield); break;*/ default: break; } - bsdf = other.bsdf; - emitter = other.emitter; - return *this; } - inline CElementShape& operator=(CElementShape&& other) + template + inline void visit(Visitor&& visitor) const { - IElement::operator=(std::move(other)); - std::swap(transform,other.transform); - std::swap(type,other.type); - switch (type) - { - case Type::CUBE: - std::swap(cube,other.cube); - break; - case Type::SPHERE: - std::swap(sphere,other.sphere); - break; - case Type::CYLINDER: - std::swap(cylinder,other.cylinder); - break; - case Type::RECTANGLE: - std::swap(rectangle,other.rectangle); - break; - case Type::DISK: - std::swap(disk,other.disk); - break; - case Type::OBJ: - std::swap(obj,other.obj); - break; - case Type::PLY: - std::swap(ply,other.ply); - break; - case Type::SERIALIZED: - std::swap(serialized,other.serialized); - break; - case Type::SHAPEGROUP: - std::swap(shapegroup,other.shapegroup); - break; - case Type::INSTANCE: - std::swap(instance,other.instance); - break;/* - case Type::HAIR: - std::swap(hair,other.hair); - break; - case Type::HEIGHTFIELD: - std::swap(heightfield,other.heightfield); - break;*/ - default: - break; - } - std::swap(bsdf,other.bsdf); - std::swap(emitter,other.emitter); + const_cast(this)->visit([&](T& var)->void + { + visitor(const_cast(var)); + } + ); + } + + inline CElementShape& operator=(const CElementShape& other) + { + IElement::operator=(other); + transform = other.transform; + type = other.type; + IElement::copyVariant(this,&other); + bsdf = other.bsdf; + emitter = other.emitter; return *this; } - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override; - IElement::Type getType() const override { return IElement::Type::SHAPE; } - std::string getLogName() const override { return "shape"; } + bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override; + + constexpr static inline auto ElementType = IElement::Type::SHAPE; + inline IElement::Type getType() const override { return ElementType; } + inline std::string getLogName() const override { return "shape"; } - inline core::matrix3x4SIMD getAbsoluteTransform() const + inline hlsl::float32_t3x4 getTransform() const { - auto local = transform.matrix.extractSub3x4(); - // TODO restore at some point (and make it actually work??) - // note: INSTANCE can only contain SHAPEGROUP and the latter doesnt have its own transform + // explicit truncation + auto local = hlsl::float32_t3x4(transform.matrix); + + // SHAPEGROUP cannot have its own transformation + assert(type!=Type::SHAPEGROUP || hlsl::math::linalg::diagonal(1)==local); - //if (type==CElementShape::INSTANCE && instance.parent) - // return core::concatenateBFollowedByA(local,instance.parent->getAbsoluteTransform()); return local; } @@ -247,7 +260,7 @@ class CElementShape : public IElement } - bool processChildData(IElement* _child, const std::string& name) override; + bool processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) override; // Type type; @@ -261,7 +274,7 @@ class CElementShape : public IElement };*/ union { - Base cube; + Cube cube; Sphere sphere; Cylinder cylinder; Base rectangle; @@ -281,7 +294,4 @@ class CElementShape : public IElement } -} -} - -#endif \ No newline at end of file +#endif diff --git a/include/nbl/ext/MitsubaLoader/CElementTexture.h b/include/nbl/ext/MitsubaLoader/CElementTexture.h index 1f3dc3ad7a..bf986f4ddb 100644 --- a/include/nbl/ext/MitsubaLoader/CElementTexture.h +++ b/include/nbl/ext/MitsubaLoader/CElementTexture.h @@ -1,79 +1,56 @@ -// 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_MISTUBA_LOADER_C_ELEMENT_TEXTURE_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_TEXTURE_H_INCLUDED_ -#ifndef __C_ELEMENT_TEXTURE_H_INCLUDED__ -#define __C_ELEMENT_TEXTURE_H_INCLUDED__ #include "nbl/ext/MitsubaLoader/PropertyElement.h" #include "nbl/ext/MitsubaLoader/IElement.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ +namespace nbl::ext::MitsubaLoader +{ class CElementTexture : public IElement { public: struct FloatOrTexture { - FloatOrTexture(CElementTexture* _tex) + inline FloatOrTexture(CElementTexture* _tex) { - value.type = SPropertyElementData::Type::INVALID; + value = std::numeric_limits::quiet_NaN(); texture = _tex; } - FloatOrTexture(float _value) + inline FloatOrTexture(const float _value) { - value.type = SPropertyElementData::Type::FLOAT; - value.fvalue = _value; + value = _value; texture = nullptr; } - FloatOrTexture(const SPropertyElementData& _other) : FloatOrTexture(nullptr) - { - operator=(_other); - } - FloatOrTexture(SPropertyElementData&& _other) : FloatOrTexture(nullptr) - { - operator=(std::move(_other)); - } - inline FloatOrTexture& operator=(const SPropertyElementData& _other) - { - return operator=(SPropertyElementData(_other)); - } - inline FloatOrTexture& operator=(SPropertyElementData&& _other) - { - switch (_other.type) - { - case SPropertyElementData::Type::INVALID: - case SPropertyElementData::Type::FLOAT: - value = std::move(_other); - break; - default: - _NBL_DEBUG_BREAK_IF(true); - break; - } - return *this; - } + inline FloatOrTexture(const FloatOrTexture&) = default; - SPropertyElementData value = {}; - CElementTexture* texture = nullptr; // only used if value.type==INVALID + inline FloatOrTexture& operator=(const FloatOrTexture&) = default; + + float value = 0.f; + CElementTexture* texture = nullptr; }; - struct SpectrumOrTexture : FloatOrTexture + struct SpectrumOrTexture { - SpectrumOrTexture(CElementTexture* _tex) : FloatOrTexture(_tex) {} - SpectrumOrTexture(float _value) : FloatOrTexture(_value) {} - SpectrumOrTexture(const SPropertyElementData& _other) : SpectrumOrTexture(nullptr) + inline SpectrumOrTexture(CElementTexture* _tex) + { + value.type = SPropertyElementData::Type::INVALID; + texture = _tex; + } + inline SpectrumOrTexture(const SPropertyElementData& _other) : SpectrumOrTexture(nullptr) { operator=(_other); } - SpectrumOrTexture(SPropertyElementData&& _other) : SpectrumOrTexture(nullptr) + inline SpectrumOrTexture(SPropertyElementData&& _other) : SpectrumOrTexture(nullptr) { operator=(std::move(_other)); } + inline SpectrumOrTexture(const float _value) : SpectrumOrTexture(SPropertyElementData{_value}) {} + inline SpectrumOrTexture& operator=(const SPropertyElementData& _other) { return operator=(SPropertyElementData(_other)); @@ -94,8 +71,12 @@ class CElementTexture : public IElement _NBL_DEBUG_BREAK_IF(true); break; } + texture = nullptr; return *this; } + + SPropertyElementData value = {}; + CElementTexture* texture = nullptr; }; enum Type @@ -109,8 +90,19 @@ class CElementTexture : public IElement //WIREFRAME, //CURVATURE }; + static inline core::unordered_map compStringToTypeMap() + { + return { + {"bitmap", CElementTexture::Type::BITMAP}, + {"scale", CElementTexture::Type::SCALE} + }; + } + struct Bitmap { + constexpr static inline Type VariantType = Type::BITMAP; + constexpr static inline uint16_t MaxPathLen = 1024u; + enum WRAP_MODE { REPEAT, @@ -137,18 +129,18 @@ class CElementTexture : public IElement Z*/ }; - SPropertyElementData filename; - WRAP_MODE wrapModeU = REPEAT; - WRAP_MODE wrapModeV = REPEAT; - float gamma = NAN; + char filename[MaxPathLen]; + WRAP_MODE wrapModeU = REPEAT; + WRAP_MODE wrapModeV = REPEAT; + float gamma = NAN; FILTER_TYPE filterType = EWA; - float maxAnisotropy = 20.f; + float maxAnisotropy = 20.f; //bool cache = false; - float uoffset = 0.f; - float voffset = 0.f; - float uscale = 1.f; - float vscale = 1.f; - CHANNEL channel = INVALID; + float uoffset = 0.f; + float voffset = 0.f; + float uscale = 1.f; + float vscale = 1.f; + CHANNEL channel = INVALID; }; struct MetaTexture { @@ -156,95 +148,69 @@ class CElementTexture : public IElement }; struct Scale : MetaTexture { - float scale; + constexpr static inline Type VariantType = Type::SCALE; + + // only monochrome scaling for now! + float scale = 1.f; }; - CElementTexture(const char* id) : IElement(id), type(Type::INVALID) + // + using variant_list_t = core::type_list; + // + static AddPropertyMap compAddPropertyMap(); + + // + inline CElementTexture(const char* id) : IElement(id), type(Type::INVALID) { } - CElementTexture(const CElementTexture& other) : CElementTexture("") + inline CElementTexture(const CElementTexture& other) : CElementTexture("") { operator=(other); } - CElementTexture(CElementTexture&& other) : CElementTexture("") + inline virtual ~CElementTexture() { - operator=(std::move(other)); } - virtual ~CElementTexture() - { - } - - inline CElementTexture& operator=(const CElementTexture& other) + + template + inline void visit(Visitor&& func) { - IElement::operator=(other); - type = other.type; switch (type) { case CElementTexture::Type::BITMAP: - bitmap = other.bitmap; + func(bitmap); break; - //case CElementTexture::Type::CHECKERBOARD: - //checkerboard = CheckerBoard(); - //break; - //case CElementTexture::Type::GRID: - //grid = Grid(); - //break; case CElementTexture::Type::SCALE: - scale = other.scale; + func(scale); break; - //case CElementTexture::Type::VERTEXCOLOR: - //vertexcolor = VertexColor(); - //break; - //case CElementTexture::Type::WIREFRAME: - //wireframe = Wireframe(); - //break; - //case CElementTexture::Type::CURVATURE: - //curvature = Curvature(); - //break; default: break; } - return *this; } - inline CElementTexture& operator=(CElementTexture&& other) + template + inline void visit(Visitor&& visitor) const + { + const_cast(this)->visit([&](T& var)->void + { + visitor(const_cast(var)); + } + ); + } + + inline CElementTexture& operator=(const CElementTexture& other) { IElement::operator=(other); type = other.type; - switch (type) - { - case CElementTexture::Type::BITMAP: - std::swap(bitmap,other.bitmap); - break; - //case CElementTexture::Type::CHECKERBOARD: - //checkerboard = CheckerBoard(); - //break; - //case CElementTexture::Type::GRID: - //grid = Grid(); - //break; - case CElementTexture::Type::SCALE: - std::swap(scale,other.scale); - break; - //case CElementTexture::Type::VERTEXCOLOR: - //vertexcolor = VertexColor(); - //break; - //case CElementTexture::Type::WIREFRAME: - //wireframe = Wireframe(); - //break; - //case CElementTexture::Type::CURVATURE: - //curvature = Curvature(); - //break; - default: - break; - } + IElement::copyVariant(this,&other); return *this; } - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override; - IElement::Type getType() const override { return IElement::Type::TEXTURE; } - std::string getLogName() const override { return "texture"; } + bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override; + + constexpr static inline auto ElementType = IElement::Type::TEXTURE; + inline IElement::Type getType() const override { return ElementType; } + inline std::string getLogName() const override { return "texture"; } - bool processChildData(IElement* _child, const std::string& name) override; + bool processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) override; // Type type; @@ -256,9 +222,5 @@ class CElementTexture : public IElement }; - -} } -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CElementTransform.h b/include/nbl/ext/MitsubaLoader/CElementTransform.h index d518f69e6c..de7bcd51e7 100644 --- a/include/nbl/ext/MitsubaLoader/CElementTransform.h +++ b/include/nbl/ext/MitsubaLoader/CElementTransform.h @@ -1,31 +1,29 @@ -// 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_MISTUBA_LOADER_C_ELEMENT_TRANSFORM_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_ELEMENT_TRANSFORM_H_INCLUDED_ -#ifndef __C_ELEMENT_TRANSFORM_H_INCLUDED__ -#define __C_ELEMENT_TRANSFORM_H_INCLUDED__ #include "nbl/ext/MitsubaLoader/IElement.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader +namespace nbl::ext::MitsubaLoader { - -class CElementTransform : public IElement +class CElementTransform final : public IElement { public: - CElementTransform() : IElement(""), matrix() {} - virtual ~CElementTransform() {} + static AddPropertyMap compAddPropertyMap(); + + inline CElementTransform() : IElement(""), matrix(1.f) {} + inline ~CElementTransform() {} - bool addProperty(SNamedPropertyElement&& _property) override; - bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) override { return true; } - IElement::Type getType() const override { return IElement::Type::TRANSFORM; } - std::string getLogName() const override { return "transform"; } + inline bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) override {return true;} + + constexpr static inline auto ElementType = IElement::Type::TRANSFORM; + inline IElement::Type getType() const override { return ElementType; } + inline std::string getLogName() const override { return "transform"; } /* inline CElementTransform& operator=(const CElementTransform& other) { @@ -35,11 +33,8 @@ class CElementTransform : public IElement } */ - core::matrix4SIMD matrix; + hlsl::float32_t4x4 matrix; // TODO: HLSL diagonal(1.f) }; } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CMitsubaLoader.h b/include/nbl/ext/MitsubaLoader/CMitsubaLoader.h index e61ab3fa87..f918962b3c 100644 --- a/include/nbl/ext/MitsubaLoader/CMitsubaLoader.h +++ b/include/nbl/ext/MitsubaLoader/CMitsubaLoader.h @@ -1,18 +1,14 @@ // 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_EXT_MISTUBA_LOADER_C_MITSUBA_LOADER_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_MITSUBA_LOADER_H_INCLUDED_ -#ifndef __C_MITSUBA_LOADER_H_INCLUDED__ -#define __C_MITSUBA_LOADER_H_INCLUDED__ #include "nbl/asset/asset.h" -#include "IFileSystem.h" -#include "nbl/asset/utils/ICPUVirtualTexture.h" - #include "nbl/ext/MitsubaLoader/CSerializedLoader.h" -#include "nbl/ext/MitsubaLoader/CMitsubaMetadata.h" -#include "nbl/ext/MitsubaLoader/CElementShape.h" +#include "nbl/ext/MitsubaLoader/ParserUtil.h" #include "nbl/ext/MitsubaLoader/SContext.h" @@ -23,8 +19,7 @@ namespace nbl::ext::MitsubaLoader class CElementBSDF; class CMitsubaMaterialCompilerFrontend; - -// TODO: we need a GLSL to C++ compatibility wrapper +#if 0 // TODO //#include "nbl/builtin/glsl/ext/MitsubaLoader/instance_data_struct.glsl" #define uint uint32_t #define uvec2 uint64_t @@ -50,30 +45,19 @@ struct nbl_glsl_ext_Mitsuba_Loader_instance_data_t #undef mat4x3 #undef nbl_glsl_MC_material_data_t using instance_data_t = nbl_glsl_ext_Mitsuba_Loader_instance_data_t; +#endif - -class CMitsubaLoader : public asset::IRenderpassIndependentPipelineLoader +class CMitsubaLoader final : public asset::ISceneLoader { - friend class CMitsubaMaterialCompilerFrontend; - public: - //! Constructor - CMitsubaLoader(asset::IAssetManager* _manager, io::IFileSystem* _fs); +// friend class CMitsubaMaterialCompilerFrontend; - void initialize() override; - - protected: - io::IFileSystem* m_filesystem; + const ParserManager m_parser; + core::smart_refctd_ptr m_system; //! Destructor virtual ~CMitsubaLoader() = default; - static core::smart_refctd_ptr createPipelineLayout(asset::IAssetManager* _manager, const asset::ICPUVirtualTexture* _vt); - - // - core::vector getMesh(SContext& ctx, uint32_t hierarchyLevel, CElementShape* shape); - core::vector loadShapeGroup(SContext& ctx, uint32_t hierarchyLevel, const CElementShape::ShapeGroup* shapegroup, const core::matrix3x4SIMD& relTform); - SContext::shape_ass_type loadBasicShape(SContext& ctx, uint32_t hierarchyLevel, CElementShape* shape, const core::matrix3x4SIMD& relTform); - +#if 0 void cacheTexture(SContext& ctx, uint32_t hierarchyLevel, const CElementTexture* texture, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic); void cacheEmissionProfile(SContext& ctx, const CElementEmissionProfile* profile); @@ -82,24 +66,21 @@ class CMitsubaLoader : public asset::IRenderpassIndependentPipelineLoader template core::smart_refctd_ptr createDS0(const SContext& _ctx, asset::ICPUPipelineLayout* _layout, const asset::material_compiler::CMaterialCompilerGLSLBackendCommon::result_t& _compResult, Iter meshBegin, Iter meshEnd); - +#endif public: - //! Check if the file might be loaded by this class - /** Check might look into the file. - \param file File handle to check. - \return True if file seems to be loadable. */ - bool isALoadableFileFormat(io::IReadFile* _file) const override; + //! Constructor + inline CMitsubaLoader(core::smart_refctd_ptr&& _system) : m_parser(), m_system(std::move(_system)) {} - //! Returns an array of string literals terminated by nullptr - const char** getAssociatedFileExtensions() const override; + bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger=nullptr) const override; - //! Returns the assets loaded by the loader - /** Bits of the returned value correspond to each IAsset::E_TYPE - enumeration member, and the return value cannot be 0. */ - uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_MESH/*|asset::IAsset::ET_SCENE|asset::IAsset::ET_IMPLEMENTATION_SPECIFIC_METADATA*/; } + inline const char** getAssociatedFileExtensions() const override + { + static const char* ext[]{ "xml", nullptr }; + return ext; + } //! Loads an asset from an opened file, returns nullptr in case of failure. - asset::SAssetBundle loadAsset(io::IReadFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; + asset::SAssetBundle loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override=nullptr, uint32_t _hierarchyLevel=0u) override; }; } diff --git a/include/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.h b/include/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.h index 42bad88655..5ef55d4e54 100644 --- a/include/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.h +++ b/include/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.h @@ -1,13 +1,11 @@ // 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 _C_MITSUBA_MATERIAL_COMPILER_FRONTEND_H_INCLUDED_ +#define _C_MITSUBA_MATERIAL_COMPILER_FRONTEND_H_INCLUDED_ -#ifndef __C_MITSUBA_MATERIAL_COMPILER_FRONTEND_H_INCLUDED__ -#define __C_MITSUBA_MATERIAL_COMPILER_FRONTEND_H_INCLUDED__ -#include "nbl/core/Types.h" - -#include "nbl/asset/material_compiler/IR.h" +//#include "nbl/asset/material_compiler/IR.h" #include "nbl/ext/MitsubaLoader/CElementBSDF.h" #include "nbl/ext/MitsubaLoader/CElementEmitter.h" @@ -23,6 +21,7 @@ struct SContext; class CMitsubaMaterialCompilerFrontend { public: +#ifdef 0 using IRNode = asset::material_compiler::IR::INode; using EmitterNode = asset::material_compiler::IR::CEmitterNode; enum E_IMAGE_VIEW_SEMANTIC : uint8_t @@ -60,8 +59,8 @@ class CMitsubaMaterialCompilerFrontend tex_ass_type getErrorTexture(const E_IMAGE_VIEW_SEMANTIC semantic) const; IRNode* createIRNode(asset::material_compiler::IR* ir, const CElementBSDF* _bsdf); +#endif }; } - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/CMitsubaMetadata.h b/include/nbl/ext/MitsubaLoader/CMitsubaMetadata.h index 087d59b772..9ffdd8f7fd 100644 --- a/include/nbl/ext/MitsubaLoader/CMitsubaMetadata.h +++ b/include/nbl/ext/MitsubaLoader/CMitsubaMetadata.h @@ -1,29 +1,22 @@ -// 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_MISTUBA_LOADER_C_MITSUBA_METADATA_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_MITSUBA_METADATA_H_INCLUDED_ -#ifndef __NBL_C_MITSUBA_METADATA_H_INCLUDED__ -#define __NBL_C_MITSUBA_METADATA_H_INCLUDED__ -#include "nbl/core/compile_config.h" #include "nbl/asset/metadata/IAssetMetadata.h" #include "nbl/asset/ICPUImage.h" -#include "nbl/ext/MitsubaLoader/SContext.h" -#include "nbl/ext/MitsubaLoader/CElementEmitter.h" #include "nbl/ext/MitsubaLoader/CElementIntegrator.h" #include "nbl/ext/MitsubaLoader/CElementSensor.h" #include "nbl/ext/MitsubaLoader/CElementShape.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ -//! A class to derive mitsuba mesh loader metadata objects from +namespace nbl::ext::MitsubaLoader +{ +//! A class to derive mitsuba scene loader metadata objects from class CMitsubaMetadata : public asset::IAssetMetadata { public: @@ -32,179 +25,77 @@ class CMitsubaMetadata : public asset::IAssetMetadata public: std::string m_id; }; - class CDerivativeMap : public asset::IImageMetadata + class IGeometry : public CID { public: - CDerivativeMap() : m_scale(1.f) {} - explicit CDerivativeMap(float scale) : m_scale(scale) {} + inline IGeometry() : CID(), type(CElementShape::Type::INVALID) {} + inline ~IGeometry() = default; - float m_scale; + CElementShape::Type type; }; - class CRenderpassIndependentPipeline : public asset::IRenderpassIndependentPipelineMetadata + class CPolygonGeometry final : public asset::IPolygonGeometryMetadata, public IGeometry { public: - CRenderpassIndependentPipeline() : IRenderpassIndependentPipelineMetadata(), m_ds0() {} - template - CRenderpassIndependentPipeline(core::smart_refctd_ptr&& _ds0, Args&&... args) : IRenderpassIndependentPipelineMetadata(std::forward(args)...), m_ds0(std::move(_ds0)) - { - } + inline CPolygonGeometry() : asset::IPolygonGeometryMetadata(), IGeometry() {} + inline CPolygonGeometry(CPolygonGeometry&& other) : CPolygonGeometry() {operator=(std::move(other));} + inline ~CPolygonGeometry() = default; - inline CRenderpassIndependentPipeline& operator=(CRenderpassIndependentPipeline&& other) + inline CPolygonGeometry& operator=(CPolygonGeometry&& other) { - IRenderpassIndependentPipelineMetadata::operator=(std::move(other)); - std::swap(m_ds0, other.m_ds0); + asset::IPolygonGeometryMetadata::operator=(std::move(other)); + IGeometry::operator=(std::move(other)); return *this; } - - core::smart_refctd_ptr m_ds0; }; - class CMesh : public asset::IMeshMetadata, public CID + class CGeometryCollection final : public asset::IGeometryCollectionMetadata, public CID { public: - CMesh() : IMeshMetadata(), CID(), m_instanceAuxData(nullptr,nullptr), type(CElementShape::Type::INVALID) {} - ~CMesh() {} - - struct SInstanceAuxilaryData - { - SInstanceAuxilaryData& operator=(SInstanceAuxilaryData&& other) - { - frontEmitter = std::move(other.frontEmitter); - backEmitter = std::move(other.backEmitter); - bsdf = std::move(other.bsdf); - return *this; - } - - CElementEmitter frontEmitter; // type is invalid if not used - CElementEmitter backEmitter; // type is invalid if not used - CMitsubaMaterialCompilerFrontend::front_and_back_t bsdf; - }; - - core::SRange m_instanceAuxData; - - CElementShape::Type type; + inline CGeometryCollection() : asset::IGeometryCollectionMetadata(), CID() {} + inline ~CGeometryCollection() = default; }; + struct SGlobal { public: - SGlobal() : m_integrator("invalid") {}// TODO - - inline uint32_t getVTStorageViewCount() const { return m_VT->getFloatViews().size(); } + inline SGlobal() : m_integrator("invalid") {} CElementIntegrator m_integrator; core::vector m_sensors; - core::vector m_emitters; - core::smart_refctd_ptr m_VT; - core::smart_refctd_ptr m_ds0; - core::vector> m_envMapImages; - //has to go after #version and before required user-provided descriptors and functions - std::string m_materialCompilerGLSL_declarations; - //has to go after required user-provided descriptors and functions and before the rest of shader (especially entry point function) - std::string m_materialCompilerGLSL_source; } m_global; - CMitsubaMetadata() : - IAssetMetadata(), m_metaPplnStorage(), m_semanticStorage(), m_metaPplnStorageIt(nullptr), - m_metaMeshStorage(), m_metaMeshInstanceStorage(), m_metaMeshInstanceAuxStorage(), - m_meshStorageIt(nullptr), m_instanceStorageIt(nullptr), m_instanceAuxStorageIt(nullptr) - { - } + inline CMitsubaMetadata() : IAssetMetadata(), m_metaPolygonGeometryStorage() {} - _NBL_STATIC_INLINE_CONSTEXPR const char* LoaderName = "ext::MitsubaLoader::CMitsubaLoader"; - const char* getLoaderName() const override { return LoaderName; } + constexpr static inline const char* LoaderName = "ext::MitsubaLoader::CMitsubaLoader"; + const char* getLoaderName() const override {return LoaderName;} - //! - inline const CRenderpassIndependentPipeline* getAssetSpecificMetadata(const asset::ICPURenderpassIndependentPipeline* asset) const + // add more overloads when more asset implementations of IGeometry exist + inline const CPolygonGeometry* getAssetSpecificMetadata(const asset::ICPUPolygonGeometry* asset) const { const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); - } - inline const CMesh* getAssetSpecificMetadata(const asset::ICPUMesh* asset) const - { - const auto found = IAssetMetadata::getAssetSpecificMetadata(asset); - return static_cast(found); + return static_cast(found); } private: - friend class CMitsubaLoader; - - meta_container_t m_metaPplnStorage; - core::smart_refctd_dynamic_array m_semanticStorage; - CRenderpassIndependentPipeline* m_metaPplnStorageIt; - - meta_container_t m_metaMeshStorage; - core::smart_refctd_dynamic_array m_metaMeshInstanceStorage; - core::smart_refctd_dynamic_array m_metaMeshInstanceAuxStorage; - CMesh* m_meshStorageIt; - CMesh::SInstance* m_instanceStorageIt; - CMesh::SInstanceAuxilaryData* m_instanceAuxStorageIt; - - meta_container_t m_metaDerivMapStorage; - CDerivativeMap* m_metaDerivMapStorageIt; - - inline void reservePplnStorage(uint32_t pplnCount, core::smart_refctd_dynamic_array&& _semanticStorage) + friend struct SContext; + struct SGeometryMetaPair { - m_metaPplnStorage = IAssetMetadata::createContainer(pplnCount); - m_semanticStorage = std::move(_semanticStorage); - m_metaPplnStorageIt = m_metaPplnStorage->begin(); - } - inline void reserveMeshStorage(uint32_t meshCount, uint32_t instanceCount) - { - m_metaMeshStorage = IAssetMetadata::createContainer(meshCount); - m_metaMeshInstanceStorage = IAssetMetadata::createContainer(instanceCount); - m_metaMeshInstanceAuxStorage = IAssetMetadata::createContainer(instanceCount); - m_meshStorageIt = m_metaMeshStorage->begin(); - m_instanceStorageIt = m_metaMeshInstanceStorage->begin(); - m_instanceAuxStorageIt = m_metaMeshInstanceAuxStorage->begin(); - } - inline void reserveDerivMapStorage(uint32_t count) - { - m_metaDerivMapStorage = IAssetMetadata::createContainer(count); - m_metaDerivMapStorageIt = m_metaDerivMapStorage->begin(); - } - inline void addPplnMeta(const asset::ICPURenderpassIndependentPipeline* ppln, core::smart_refctd_ptr&& _ds0) - { - *m_metaPplnStorageIt = CMitsubaMetadata::CRenderpassIndependentPipeline(std::move(_ds0),core::SRange(m_semanticStorage->begin(),m_semanticStorage->end())); - IAssetMetadata::insertAssetSpecificMetadata(ppln,m_metaPplnStorageIt); - m_metaPplnStorageIt++; - } - template - inline uint32_t addMeshMeta(const asset::ICPUMesh* mesh, std::string&& id, const CElementShape::Type type, InstanceIterator instancesBegin, InstanceIterator instancesEnd) + core::smart_refctd_ptr geom; + CMitsubaMetadata::CPolygonGeometry meta; + }; + inline void setPolygonGeometryMeta(core::unordered_map&& container) { - auto instanceStorageBegin = m_instanceStorageIt; - auto instanceAuxStorageBegin = m_instanceAuxStorageIt; - - auto* meta = m_meshStorageIt++; - meta->m_id = std::move(id); + const uint32_t count = container.size(); + m_metaPolygonGeometryStorage = IAssetMetadata::createContainer(count); + auto outIt = m_metaPolygonGeometryStorage->begin(); + for (auto& el : container) { - // copy instance data - for (auto it=instancesBegin; it!=instancesEnd; ++it) - { - auto& inst = it->second; - (m_instanceStorageIt++)->worldTform = inst.tform; - *(m_instanceAuxStorageIt++) = { - inst.emitter.front, - inst.emitter.back, - inst.bsdf - }; - } - meta->m_instances = { instanceStorageBegin,m_instanceStorageIt }; - meta->m_instanceAuxData = { instanceAuxStorageBegin,m_instanceAuxStorageIt }; + *outIt = std::move(el.second.meta); + IAssetMetadata::insertAssetSpecificMetadata(el.second.geom.get(),outIt++); } - meta->type = type; - IAssetMetadata::insertAssetSpecificMetadata(mesh,meta); - - return meta->m_instances.size(); - } - inline void addDerivMapMeta(const asset::ICPUImage* derivmap, float scale) - { - auto* meta = m_metaDerivMapStorageIt++; - meta->m_scale = scale; - IAssetMetadata::insertAssetSpecificMetadata(derivmap, meta); } + + meta_container_t m_metaPolygonGeometryStorage; }; } -} -} - #endif diff --git a/include/nbl/ext/MitsubaLoader/CSerializedLoader.h b/include/nbl/ext/MitsubaLoader/CSerializedLoader.h index 1ac08aba79..44bb0739c5 100644 --- a/include/nbl/ext/MitsubaLoader/CSerializedLoader.h +++ b/include/nbl/ext/MitsubaLoader/CSerializedLoader.h @@ -21,7 +21,7 @@ class CSerializedLoader final : public asset::IGeometryLoader public: inline CSerializedLoader() = default; - inline bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger = nullptr) const override + inline bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger=nullptr) const override { FileHeader header; diff --git a/include/nbl/ext/MitsubaLoader/IElement.h b/include/nbl/ext/MitsubaLoader/IElement.h index 8f6fa24ea7..a09761e87f 100644 --- a/include/nbl/ext/MitsubaLoader/IElement.h +++ b/include/nbl/ext/MitsubaLoader/IElement.h @@ -1,23 +1,34 @@ -// 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_MISTUBA_LOADER_I_ELEMENT_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_I_ELEMENT_H_INCLUDED_ -#ifndef __I_ELEMENT_H_INCLUDED__ -#define __I_ELEMENT_H_INCLUDED__ +#include "nbl/core/algorithm/utility.h" #include "nbl/asset/interchange/IAssetLoader.h" + #include "nbl/ext/MitsubaLoader/PropertyElement.h" -namespace nbl +namespace nbl::ext::MitsubaLoader { -namespace ext +class CMitsubaMetadata; + +namespace impl { -namespace MitsubaLoader +template class Pred, typename... Args> +struct ToUnaryPred { + template + struct type : bool_constant::value> {}; +}; - -class CMitsubaMetadata; +template +struct mpl_of_passing; +template +using mpl_of_passing_t = mpl_of_passing::type; +} class IElement { @@ -44,8 +55,6 @@ class IElement TRANSFORM, ANIMATION }; - public: - std::string id; IElement(const char* _id) : id(_id ? _id:"") {} virtual ~IElement() = default; @@ -53,15 +62,15 @@ class IElement virtual IElement::Type getType() const = 0; virtual std::string getLogName() const = 0; - virtual bool addProperty(SNamedPropertyElement&& _property) = 0; - virtual bool onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) = 0; + virtual bool onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) = 0; //! default implementation for elements that doesnt have any children - virtual bool processChildData(IElement* _child, const std::string& name) + virtual bool processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) { return !_child; } + // - static inline bool getTypeIDAndNameStrings(std::add_lvalue_reference::type outType, std::add_lvalue_reference::type outID, std::string& name, const char** _atts) + static inline bool getTypeIDAndNameStrings(std::add_lvalue_reference_t outType, std::add_lvalue_reference_t outID, std::string& name, const char** _atts) { outType = nullptr; outID = nullptr; @@ -81,7 +90,7 @@ class IElement } return outType; } - static inline bool getIDAndName(std::add_lvalue_reference::type id, std::string& name, const char** _atts) + static inline bool getIDAndName(std::add_lvalue_reference_t id, std::string& name, const char** _atts) { const char* thrownAwayType; getTypeIDAndNameStrings(thrownAwayType,id,name,_atts); @@ -111,10 +120,124 @@ class IElement return _atts[attrCount]; } + + // if we used `variant` instead of union we could default implement this + //template + //static inline void defaultVisit(Derived* this) + //{ + // generated switch / visit of `Variant` + //} + template + static inline void copyVariant(Derived* to, const Derived* from) + { + to->visit([from](auto& selfEl)->void + { + from->visit([&selfEl](const auto& otherEl)->void + { + if constexpr (std::is_same_v,std::decay_t>) + selfEl = otherEl; + } + ); + } + ); + } + + // could move it to `nbl/builtin/hlsl/mpl` + template + struct mpl_array + { + constexpr static inline Type data[] = { values... }; + }; + // + template + struct AddPropertyCallback + { + using element_t = Derived; + // TODO: list or map of supported variants (if `visit` is present) + using func_t = bool(*)(Derived*,SNamedPropertyElement&&,const system::logger_opt_ptr); + + inline bool operator()(Derived* d, SNamedPropertyElement&& p, const system::logger_opt_ptr l) const {return func(d,std::move(p),l);} + + func_t func; + // will usually point at + std::span allowedVariantTypes = {}; + }; + template + using PropertyNameCallbackMap = core::unordered_map,core::CaseInsensitiveHash,core::CaseInsensitiveEquals>; + template + class AddPropertyMap + { + template + inline void registerCallback(const SNamedPropertyElement::Type type, std::string&& propertyName, AddPropertyCallback cb) + { + if constexpr (sizeof...(types)) + cb.allowedVariantTypes = mpl_array::data; + registerCallback(type,std::move(propertyName),cb); + } + + public: + using element_type = Derived; + + inline void registerCallback(const SNamedPropertyElement::Type type, std::string&& propertyName, const AddPropertyCallback& cb) + { + auto [nameIt,inserted] = byPropertyType[type].emplace(std::move(propertyName),cb); + assert(inserted); + } + template class Pred, typename... Args> + inline void registerCallback(const SNamedPropertyElement::Type type, std::string&& propertyName, AddPropertyCallback::func_t cb) + { + AddPropertyCallback callback = {.func=cb}; + using UnaryPred = impl::ToUnaryPred; + using passing_types = core::filter_t; + if constexpr (core::type_list_size_v) + callback.allowedVariantTypes = impl::mpl_of_passing_t::data; + registerCallback(type,std::move(propertyName),callback); + } + + std::array,SNamedPropertyElement::Type::INVALID> byPropertyType = {}; + }; + // + template + struct ProcessChildCallback + { + using element_t = Derived; + // TODO: list or map of supported variants (if `visit` is present) + using func_t = bool(*)(Derived*,IElement* _child,const system::logger_opt_ptr); + + inline bool operator()(Derived* d, IElement* _child, const system::logger_opt_ptr l) const {return func(d,_child,l);} + + func_t func; + // TODO: allowed IElement types + }; + template + using ProcessChildCallbackMap = core::unordered_map,core::CaseInsensitiveHash,core::CaseInsensitiveEquals>; + + // members + std::string id; + + protected: + static inline void setLimitedString(const std::string_view memberName, std::span out, const SNamedPropertyElement& _property, const system::logger_opt_ptr logger) + { + auto len = strlen(_property.svalue); + if (len>=out.size()) + logger.log( + "String property assigned to %s is too long, max allowed length %d, is %d, property value: \"%s\"", + system::ILogger::ELL_ERROR,memberName.data(),out.size(),len,_property.svalue + ); + len = std::min(out.size()-1,len); + memcpy(out.data(),_property.svalue,len); + out[len] = 0; + } }; -} -} +namespace impl +{ +template +struct mpl_of_passing> +{ + using type = IElement::mpl_array; +}; } +} #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/ParserUtil.h b/include/nbl/ext/MitsubaLoader/ParserUtil.h index 65c8e1fb58..5e33951348 100644 --- a/include/nbl/ext/MitsubaLoader/ParserUtil.h +++ b/include/nbl/ext/MitsubaLoader/ParserUtil.h @@ -1,50 +1,38 @@ // 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_EXT_MISTUBA_LOADER_I_PARSER_UTIL_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_I_PARSER_UTIL_H_INCLUDED_ -#ifndef __I_PARSER_UTIL_H_INCLUDED__ -#define __I_PARSER_UTIL_H_INCLUDED__ - -//#include "nbl/core/core.h" - -//#include "IFileSystem.h" #include "nbl/asset/interchange/IAssetLoader.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" #include "nbl/ext/MitsubaLoader/CMitsubaMetadata.h" - -#include "expat/lib/expat.h" +#include "nbl/ext/MitsubaLoader/CElementIntegrator.h" +#include "nbl/ext/MitsubaLoader/CElementSensor.h" +#include "nbl/ext/MitsubaLoader/CElementFilm.h" +#include "nbl/ext/MitsubaLoader/CElementRFilter.h" +#include "nbl/ext/MitsubaLoader/CElementSampler.h" +#include "nbl/ext/MitsubaLoader/CElementShape.h" +#include "nbl/ext/MitsubaLoader/CElementBSDF.h" +#include "nbl/ext/MitsubaLoader/CElementTexture.h" +#include "nbl/ext/MitsubaLoader/CElementEmitter.h" +#include "nbl/ext/MitsubaLoader/CElementEmissionProfile.h" #include +// don't leak expat headers +struct XML_ParserStruct; +typedef struct XML_ParserStruct* XML_Parser; -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ - - -class ParserLog +namespace nbl::ext::MitsubaLoader { -public: - static inline void setLogger(const system::logger_opt_ptr& logger) { ParserLog::logger = logger; }; - - /*prints this message: - Mitsuba loader error: - Invalid .xml file structure: message */ - static void invalidXMLFileStructure(const std::string& errorMessage); - -private: - static system::logger_opt_ptr logger; -}; - +class IElement; +// TODO: replace with common Class for Material Compiler V3 Node Pool template -class ElementPool // : public std::tuple...> +class ElementPool final : public core::Unmovable// similar to : public std::tuple...> { core::SimpleBlockBasedAllocator,core::aligned_allocator> poolAllocator; public: @@ -59,80 +47,130 @@ class ElementPool // : public std::tuple...> }; //struct, which will be passed to expat handlers as user data (first argument) see: XML_StartElementHandler or XML_EndElementHandler in expat.h -class ParserManager +class ParserManager final { - protected: - struct Context - { - ParserManager* manager; - XML_Parser parser; - system::path currentXMLDir; - }; public: //! Constructor - ParserManager(system::ISystem* _system, asset::IAssetLoader::IAssetLoaderOverride* _override) : - m_system(_system), m_override(_override), m_sceneDeclCount(0), - m_metadata(core::make_smart_refctd_ptr()) - { - } + ParserManager(); // static void elementHandlerStart(void* _data, const char* _el, const char** _atts); static void elementHandlerEnd(void* _data, const char* _el); - // - inline void killParseWithError(const Context& ctx, const std::string& message) + struct Params { - _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure(message); - XML_StopParser(ctx.parser, false); - } - - bool parse(system::IFile* _file, const system::logger_opt_ptr& _logger); - - void parseElement(const Context& ctx, const char* _el, const char** _atts); - - void onEnd(const Context& ctx, const char* _el); - - // - core::vector > shapegroups; - // - core::smart_refctd_ptr m_metadata; - - private: - // - void processProperty(const Context& ctx, const char* _el, const char** _atts); - - // - system::ISystem* m_system; - asset::IAssetLoader::IAssetLoaderOverride* m_override; - // - uint32_t m_sceneDeclCount; - // - ElementPool< + system::logger_opt_ptr logger; + // for opening included XML files + system::ISystem* system; + asset::IAssetLoader::IAssetLoaderOverride* _override; + }; + template + struct SNamedElement + { + ElementT* element = nullptr; + core::string name = {}; + }; + struct Result final + { + public: + explicit inline operator bool() const {return bool(metadata);} + + // note that its shared between per-file contexts + core::smart_refctd_ptr metadata = nullptr; + // + using emitter_t = SNamedElement; + core::vector emitters = {}; + // + using shape_group_t = SNamedElement; + core::vector shapegroups = {}; + // + hlsl::float32_t3 ambient = {0,0,0}; + + private: + friend class ParserManager; + // TODO: This leaks memory all over the place because destructors are not ran! + std::unique_ptr> objects = std::make_unique>(); + }; + Result parse(system::IFile* _file, const Params& _params) const; + + // Properties are simple XML nodes which are not `IElement` and neither children of an` IElement` + // If we match any ` propertyElements; + const CPropertyElementManager propertyElementManager; + + using supported_elements_t = core::type_list< CElementIntegrator, CElementSensor, CElementFilm, CElementRFilter, CElementSampler, CElementShape, + CElementTransform, CElementBSDF, CElementTexture, - CElementEmitter - > objects; - // aliases and names - core::unordered_map handles; + CElementEmitter, + CElementEmissionProfile + >; - /*stack of currently processed elements - each element of index N is parent of the element of index N+1 - the scene element is a parent of all elements of index 0 */ - core::stack > elements; + private: + const core::tuple_transform_t addPropertyMaps; - friend class CElementFactory; + // the XMLs can include each other, so this stores the stuff across files + struct SessionContext + { + // prints this message: + // Mitsuba loader error: + // Invalid .xml file structure: message + inline void invalidXMLFileStructure(const std::string& errorMessage) const + { + ::nbl::ext::MitsubaLoader::invalidXMLFileStructure(params->logger,errorMessage); + } + // meant for parsing one file in an include chain + bool parse(system::IFile* _file); + + Result* const result; + const Params* const params; + const ParserManager* const manager; + // + uint32_t sceneDeclCount = 0; + // aliases and names (in Mitsbua XML you can give nodes names and `ref` them) + core::unordered_map handles = {}; + // stack of currently processed elements, each element of index N is parent of the element of index N+1 + // the scene element is a parent of all elements of index 0 + using named_element_t = SNamedElement; + core::stack elements = {}; + }; + // This is for a single XML File + struct XMLContext + { + // + void killParseWithError(const std::string& message) const; + void parseElement(const char* _el, const char** _atts); + void onEnd(const char* _el); + + SessionContext* const session; + // + const system::path currentXMLDir; + // + XML_Parser parser; + }; + + struct SElementCreator + { + // we still push nullptr (failed creation) onto the stack, we only stop parse on catastrphic failure later on if a use of the element pops up + // this is why we don't need XMLCOntext for `killParseWithError` + using func_t = SessionContext::named_element_t(*)(const char**/*attributes*/,SessionContext*); + func_t create; + bool retvalGoesOnStack; + }; + const core::unordered_map createElementTable; + // + template + struct CreateElement; + // + static SessionContext::named_element_t processAlias(const char** _atts, SessionContext* ctx); + static SessionContext::named_element_t processRef(const char** _atts, SessionContext* ctx); }; } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/PropertyElement.h b/include/nbl/ext/MitsubaLoader/PropertyElement.h index ac257bd4b3..0d8450ff62 100644 --- a/include/nbl/ext/MitsubaLoader/PropertyElement.h +++ b/include/nbl/ext/MitsubaLoader/PropertyElement.h @@ -1,24 +1,28 @@ -// 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_MISTUBA_LOADER_PROPERTY_ELEMENT_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_PROPERTY_ELEMENT_H_INCLUDED_ -#ifndef __PROPERTY_ELEMENT_H_INCLUDED__ -#define __PROPERTY_ELEMENT_H_INCLUDED__ #include "nbl/core/declarations.h" -#include "matrix4SIMD.h" -#include +#include "nbl/builtin/hlsl/cpp_compat.hlsl" -namespace nbl -{ -namespace ext + +namespace nbl::ext::MitsubaLoader { -namespace MitsubaLoader +// maybe move somewhere +inline void invalidXMLFileStructure(system::logger_opt_ptr logger, const std::string& errorMessage) { + // TODO: print the line in the XML or something + logger.log("Mitsuba loader error - Invalid .xml file structure: \'%s\'",system::ILogger::E_LOG_LEVEL::ELL_ERROR,errorMessage.c_str()); + _NBL_DEBUG_BREAK_IF(true); +} struct SPropertyElementData { - enum Type + // TODO: enum class, and smaller type + enum Type : uint32_t { FLOAT, INTEGER, @@ -37,14 +41,12 @@ struct SPropertyElementData VECTOR, INVALID }; - - static const core::unordered_map StringToType; - _NBL_STATIC_INLINE_CONSTEXPR uint32_t MaxAttributes = 5u; - static const char* attributeStrings[Type::INVALID][MaxAttributes]; + // + constexpr static inline uint32_t MaxAttributes = 5u; inline SPropertyElementData() : type(Type::INVALID) { - std::fill(mvalue.pointer(), mvalue.pointer() + 16, 0.f); + memset(&fvalue,0,sizeof(mvalue)); } inline SPropertyElementData(const SPropertyElementData& other) : SPropertyElementData() { @@ -54,17 +56,11 @@ struct SPropertyElementData { operator=(std::move(other)); } - inline SPropertyElementData(const std::string& _type) : SPropertyElementData() - { - auto found = StringToType.find(_type); - if (found != StringToType.end()) - type = found->second; - } inline explicit SPropertyElementData(float value) : type(FLOAT) { fvalue = value; } inline explicit SPropertyElementData(int32_t value) : type(INTEGER) { ivalue = value; } inline explicit SPropertyElementData(bool value) : type(BOOLEAN) { bvalue = value; } //explicit SPropertyElementData(const std::string& value) : type(STRING) { #error } - inline explicit SPropertyElementData(Type _type, const core::vectorSIMDf& value): type(INVALID) + inline explicit SPropertyElementData(Type _type, const hlsl::float32_t4& value): type(INVALID) { switch (_type) { @@ -80,9 +76,9 @@ struct SPropertyElementData break; }; } - ~SPropertyElementData() + inline ~SPropertyElementData() { - if (type == Type::STRING) + if (type==Type::STRING) _NBL_ALIGNED_FREE((void*)svalue); } @@ -129,7 +125,7 @@ struct SPropertyElementData mvalue = other.mvalue; break; default: - std::fill(mvalue.pointer(), mvalue.pointer()+16, 0.f); + memset(&fvalue,0,sizeof(mvalue)); break; } return *this; @@ -171,17 +167,19 @@ struct SPropertyElementData mvalue = other.mvalue; break; default: - std::fill(other.mvalue.pointer(), other.mvalue.pointer() + 16, 0.f); + memset(&fvalue,0,sizeof(mvalue)); break; } return *this; } - + // TODO: enum class on the template param + template + struct get_type; template - struct get_typename; + using get_type_t = typename get_type::type; template - const typename get_typename::type& getProperty() const; + const get_type_t& getProperty() const; inline uint8_t getVectorDimension() const { @@ -201,63 +199,85 @@ struct SPropertyElementData int32_t ivalue; bool bvalue; const char* svalue; - core::vectorSIMDf vvalue; // rgb, srgb, vector, point - core::matrix4SIMD mvalue; // matrix, translate, rotate, scale, lookat + hlsl::float32_t4 vvalue; // rgb, srgb, vector, point + hlsl::float32_t4x4 mvalue; // matrix, translate, rotate, scale, lookat }; }; struct SNamedPropertyElement : SPropertyElementData { - SNamedPropertyElement() : SPropertyElementData(), name("") + inline SNamedPropertyElement() : SPropertyElementData(), name("") { } - SNamedPropertyElement(const std::string& _type) : SNamedPropertyElement() - { - auto found = SPropertyElementData::StringToType.find(_type); - if (found != SPropertyElementData::StringToType.end()) - type = found->second; - } - SNamedPropertyElement(const SNamedPropertyElement& other) : SNamedPropertyElement() + inline SNamedPropertyElement(const SNamedPropertyElement& other) : SNamedPropertyElement() { SNamedPropertyElement::operator=(other); } - SNamedPropertyElement(SNamedPropertyElement&& other) : SNamedPropertyElement() + inline SNamedPropertyElement(SNamedPropertyElement&& other) : SNamedPropertyElement() { SNamedPropertyElement::operator=(std::move(other)); } - bool initialize(const char** _atts, const char** outputMatch) + inline bool initialize(const char** _atts, const char** outputMatch) { - if (type == Type::INVALID || !_atts) + if (type==Type::INVALID || !_atts) return false; - for (auto it = _atts; *it; it++) + constexpr const char* AttributeStrings[SPropertyElementData::Type::INVALID][SPropertyElementData::MaxAttributes] = { + {"value"}, // FLOAT + {"value"}, // INTEGER + {"value"}, // BOOLEAN + {"value"}, // STRING + {"value","intent"}, // RGB + {"value","intent"}, // SRGB + {"value","intent","filename"}, // SPECTRUM + {"temperature","scale"}, // BLACKBODY + {"value"}, // MATRIX + {"x","y","z"}, // TRANSLATE + {"angle","x","y","z"}, // ROTATE + {"value","x","y","z"}, // SCALE + {"origin","target","up"}, // LOOKAT + {"x","y","z"}, // POINT + {"x","y","z","w"} // VECTOR + }; + // TODO: some magical constexpr thing to count up + //constexpr size_t AttributeCount[SPropertyElementData::Type::INVALID][SPropertyElementData::MaxAttributes] = {}; + + for (auto it=_atts; *it; it++) { - if (core::strcmpi(*it, "name") == 0) + // found the name attribute + if (core::strcmpi(*it,"name") == 0) { + // value follows the attribute name it++; if (*it) { + // next attribute is the actual name, first is just the `name=` name = *it; continue; } - else + else // no name present e.g. `name=""` return false; } - for (auto i = 0u; i < SPropertyElementData::MaxAttributes; i++) + // now go through the expected attributes + for (auto i=0u; i struct SPropertyElementData::get_typename +template<> struct SPropertyElementData::get_type { using type = float; }; -template<> struct SPropertyElementData::get_typename +template<> struct SPropertyElementData::get_type { using type = int32_t; }; -template<> struct SPropertyElementData::get_typename +template<> struct SPropertyElementData::get_type { using type = bool; }; -template<> struct SPropertyElementData::get_typename +template<> struct SPropertyElementData::get_type { using type = const char*; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::vectorSIMDf; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::vectorSIMDf; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::vectorSIMDf; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::vectorSIMDf; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::vectorSIMDf; }; -template<> struct SPropertyElementData::get_typename +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4; }; +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4; }; +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4; }; +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4; }; +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4; }; +template<> struct SPropertyElementData::get_type { using type = void; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::matrix4SIMD; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::matrix4SIMD; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::matrix4SIMD; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::matrix4SIMD; }; -template<> struct SPropertyElementData::get_typename -{ using type = core::matrix4SIMD; }; -template<> struct SPropertyElementData::get_typename +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4x4; }; +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4x4; }; +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4x4; }; +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4x4; }; +template<> struct SPropertyElementData::get_type +{ using type = hlsl::float32_t4x4; }; +template<> struct SPropertyElementData::get_type { using type = void; }; +// TODO: rewrite rest to be less `::` verbose +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return fvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return ivalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return bvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return svalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return vvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return vvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return vvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return vvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return vvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return mvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return mvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return mvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return mvalue; } +template<> inline auto SPropertyElementData::getProperty() const -> const get_type_t& +{ return mvalue; } -class CPropertyElementManager +class CPropertyElementManager final : public core::Unmovable { - public: - static std::pair createPropertyData(const char* _el, const char** _atts); + const core::unordered_map StringToType; - static bool retrieveBooleanValue(const std::string& _data, bool& success); - static core::matrix4SIMD retrieveMatrix(const std::string& _data, bool& success); - static core::vectorSIMDf retrieveVector(const std::string& _data, bool& success); - static core::vectorSIMDf retrieveHex(const std::string& _data, bool& success); + static std::optional retrieveBooleanValue(const std::string_view& _data, system::logger_opt_ptr logger); + static hlsl::float32_t4x4 retrieveMatrix(const std::string_view& _data, system::logger_opt_ptr logger); + static hlsl::float32_t4 retrieveVector(const std::string_view& _data, system::logger_opt_ptr logger); + static hlsl::float32_t4 retrieveHex(const std::string_view& _data, system::logger_opt_ptr logger); + + public: + CPropertyElementManager(); +#if 0 + inline SPropertyElementData(const std::string& _type) : SPropertyElementData() + { + auto found = StringToType.find(_type); + if (found != StringToType.end()) + type = found->second; + } + SNamedPropertyElement(const std::string& _type) : SNamedPropertyElement() + { + auto found = SPropertyElementData::StringToType.find(_type); + if (found != SPropertyElementData::StringToType.end()) + type = found->second; + } +#endif + std::optional createPropertyData(const char* _el, const char** _atts, system::logger_opt_ptr logger) const; }; } -} -} - #endif \ No newline at end of file diff --git a/include/nbl/ext/MitsubaLoader/SContext.h b/include/nbl/ext/MitsubaLoader/SContext.h index 687f97054d..f3c952935c 100644 --- a/include/nbl/ext/MitsubaLoader/SContext.h +++ b/include/nbl/ext/MitsubaLoader/SContext.h @@ -1,110 +1,60 @@ -// 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_MISTUBA_LOADER_C_MITSUBA_LOADER_CONTEXT_H_INCLUDED_ +#define _NBL_EXT_MISTUBA_LOADER_C_MITSUBA_LOADER_CONTEXT_H_INCLUDED_ -#ifndef __C_MITSUBA_LOADER_CONTEXT_H_INCLUDED__ -#define __C_MITSUBA_LOADER_CONTEXT_H_INCLUDED__ - -#include "nbl/asset/ICPUMesh.h" -#include "nbl/asset/utils/IGeometryCreator.h" -#include "nbl/asset/material_compiler/CMaterialCompilerGLSLRasterBackend.h" +#include "nbl/asset/ICPUPolygonGeometry.h" #include "nbl/asset/interchange/CIESProfileLoader.h" -#include "nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.h" -#include "nbl/ext/MitsubaLoader/CElementShape.h" +#include "nbl/ext/MitsubaLoader/CMitsubaMetadata.h" +//#include "nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader + +namespace nbl::ext::MitsubaLoader { -struct SContext +struct SContext final { public: SContext( - const asset::IGeometryCreator* _geomCreator, - const asset::IMeshManipulator* _manipulator, +// const asset::IGeometryCreator* _geomCreator, +// const asset::IMeshManipulator* _manipulator, const asset::IAssetLoader::SAssetLoadContext& _params, asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* _metadata ); - const asset::IGeometryCreator* creator; - const asset::IMeshManipulator* manipulator; + using shape_ass_type = core::smart_refctd_ptr; + shape_ass_type loadBasicShape(const uint32_t hierarchyLevel, const CElementShape* shape); + using group_ass_type = core::smart_refctd_ptr; + group_ass_type loadShapeGroup(const uint32_t hierarchyLevel, const CElementShape::ShapeGroup* shapegroup); + + inline void transferMetadata() + { + meta->setPolygonGeometryMeta(std::move(shapeCache)); + } + +// const asset::IGeometryCreator* creator; +// const asset::IMeshManipulator* manipulator; const asset::IAssetLoader::SAssetLoadContext inner; asset::IAssetLoader::IAssetLoaderOverride* override_; CMitsubaMetadata* meta; + core::smart_refctd_ptr scene; - _NBL_STATIC_INLINE_CONSTEXPR uint32_t VT_PAGE_SZ_LOG2 = 7u;//128 - _NBL_STATIC_INLINE_CONSTEXPR uint32_t VT_PHYSICAL_PAGE_TEX_TILES_PER_DIM_LOG2 = 4u;//16 - _NBL_STATIC_INLINE_CONSTEXPR uint32_t VT_PAGE_PADDING = 8u; - _NBL_STATIC_INLINE_CONSTEXPR uint32_t VT_MAX_ALLOCATABLE_TEX_SZ_LOG2 = 12u;//4096 - + private: // - using group_ass_type = core::vector>; - //core::map groupCache; + core::unordered_map groupCache; // - using shape_ass_type = core::smart_refctd_ptr; - core::map shapeCache; + core::unordered_map shapeCache; + +#if 0 // stuff that belongs in the Material Compiler backend //image, sampler using tex_ass_type = std::tuple,core::smart_refctd_ptr>; - //image, scale + //image, scale core::map,float> derivMapCache; - // - static std::string imageViewCacheKey(const CElementTexture::Bitmap& bitmap, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic) - { - std::string key = bitmap.filename.svalue; - switch (bitmap.channel) - { - case CElementTexture::Bitmap::CHANNEL::R: - key += "?rrrr"; - break; - case CElementTexture::Bitmap::CHANNEL::G: - key += "?gggg"; - break; - case CElementTexture::Bitmap::CHANNEL::B: - key += "?bbbb"; - break; - case CElementTexture::Bitmap::CHANNEL::A: - key += "?aaaa"; - break; - default: - break; - } - switch (semantic) - { - case CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT: - key += "?blend"; - break; - case CMitsubaMaterialCompilerFrontend::EIVS_NORMAL_MAP: - key += "?deriv?n"; - break; - case CMitsubaMaterialCompilerFrontend::EIVS_BUMP_MAP: - key += "?deriv?h"; - { - static const char* wrap[5] - { - "?repeat", - "?mirror", - "?clamp", - "?zero", - "?one" - }; - key += wrap[bitmap.wrapModeU]; - key += wrap[bitmap.wrapModeV]; - } - break; - default: - break; - } - key += "?view"; - return key; - } - static asset::ISampler::SParams emissionProfileSamplerParams(const CElementEmissionProfile* profile, const asset::CIESProfileMetadata& meta) { return { @@ -171,110 +121,13 @@ struct SContext return params; } - inline core::smart_refctd_ptr getSampler(const asset::ICPUSampler::SParams& params) const - { - const std::string samplerKey = samplerCacheKey(params); - const asset::IAsset::E_TYPE types[2] = {asset::IAsset::ET_SAMPLER,asset::IAsset::ET_TERMINATING_ZERO}; - auto samplerBundle = override_->findCachedAsset(samplerKey,types,inner,0u); - if (samplerBundle.getContents().empty()) - { - auto sampler = core::make_smart_refctd_ptr(params); - override_->insertAssetIntoCache(asset::SAssetBundle(nullptr,{sampler}),samplerKey,inner,0); - return sampler; - } - else - return core::smart_refctd_ptr_static_cast(samplerBundle.getContents().begin()[0]); - } - //index of root node in IR using bsdf_type = const CMitsubaMaterialCompilerFrontend::front_and_back_t; //caches instr buffer instr-wise offset (.first) and instruction count (.second) for each bsdf node core::unordered_map instrStreamCache; - - struct SInstanceData - { - SInstanceData(core::matrix3x4SIMD _tform, SContext::bsdf_type _bsdf, const std::string& _id, const CElementEmitter& _emitterFront, const CElementEmitter& _emitterBack) : - tform(_tform), bsdf(_bsdf), -#if defined(_NBL_DEBUG) || defined(_NBL_RELWITHDEBINFO) - bsdf_id(_id), -#endif - emitter{_emitterFront, _emitterBack} - {} - - core::matrix3x4SIMD tform; - SContext::bsdf_type bsdf; -#if defined(_NBL_DEBUG) || defined(_NBL_RELWITHDEBINFO) - std::string bsdf_id; #endif - struct { - // type is invalid if not used - CElementEmitter front; - CElementEmitter back; - } emitter; - }; - core::unordered_multimap mapMesh2instanceData; - - struct SPipelineCacheKey - { - asset::SVertexInputParams vtxParams; - asset::SPrimitiveAssemblyParams primParams; - - inline bool operator==(const SPipelineCacheKey& rhs) const - { - return memcmp(&vtxParams, &rhs.vtxParams, sizeof(vtxParams)) == 0 && memcmp(&primParams, &rhs.primParams, sizeof(primParams)) == 0; - } - - struct hash - { - inline size_t operator()(const SPipelineCacheKey& k) const - { - constexpr size_t BYTESZ = sizeof(k.vtxParams) + sizeof(k.primParams); - uint8_t mem[BYTESZ]{}; - uint8_t* ptr = mem; - memcpy(ptr, &k.vtxParams, sizeof(k.vtxParams)); - ptr += sizeof(k.vtxParams); - memcpy(ptr, &k.primParams, sizeof(k.primParams)); - ptr += sizeof(k.primParams); - - return std::hash{}(std::string_view(reinterpret_cast(mem), BYTESZ)); - } - }; - }; - core::unordered_map, SPipelineCacheKey::hash> pipelineCache; - - //material compiler - core::smart_refctd_ptr ir; - CMitsubaMaterialCompilerFrontend frontend; - asset::material_compiler::CMaterialCompilerGLSLRasterBackend::SContext backend_ctx; - asset::material_compiler::CMaterialCompilerGLSLRasterBackend backend; - - private: - // TODO: commonalize this to all loaders - static std::string samplerCacheKey(const asset::ICPUSampler::SParams& samplerParams) - { - std::string samplerCacheKey = "__Sampler"; - - if (samplerParams.MinFilter==asset::ISampler::ETF_LINEAR) - samplerCacheKey += "?trilinear"; - else - samplerCacheKey += "?nearest"; - - static const char* wrapModeName[] = - { - "?repeat", - "?clamp_to_edge", - "?clamp_to_border", - "?mirror", - "?mirror_clamp_to_edge", - "?mirror_clamp_to_border" - }; - samplerCacheKey += wrapModeName[samplerParams.TextureWrapU]; - samplerCacheKey += wrapModeName[samplerParams.TextureWrapV]; - - return samplerCacheKey; - } + core::smart_refctd_ptr frontIR; }; -}}} - +} #endif \ No newline at end of file diff --git a/include/nbl/ext/ScreenShot/ScreenShot.h b/include/nbl/ext/ScreenShot/ScreenShot.h index 4e71749cd7..64f5e526d1 100644 --- a/include/nbl/ext/ScreenShot/ScreenShot.h +++ b/include/nbl/ext/ScreenShot/ScreenShot.h @@ -27,7 +27,12 @@ inline core::smart_refctd_ptr createScreenShot( const ACCESS_FLAGS accessMask, const IImage::LAYOUT imageLayout) { - assert(bool(logicalDevice->getPhysicalDevice()->getQueueFamilyProperties().begin()[queue->getFamilyIndex()].queueFlags.value & IQueue::FAMILY_FLAGS::TRANSFER_BIT)); + { + const auto queueFlags = logicalDevice->getPhysicalDevice()->getQueueFamilyProperties().begin()[queue->getFamilyIndex()].queueFlags; + const auto required = core::bitflag(IQueue::FAMILY_FLAGS::TRANSFER_BIT) | IQueue::FAMILY_FLAGS::GRAPHICS_BIT | IQueue::FAMILY_FLAGS::COMPUTE_BIT; + if (!queueFlags.hasAnyFlag(required)) + logicalDevice->getLogger()->log("ScreenShot: queue family %u lacks transfer/graphics/compute flags; continuing anyway.", system::ILogger::ELL_WARNING, queue->getFamilyIndex()); + } auto fetchedImageViewParmas = gpuImageView->getCreationParameters(); auto gpuImage = fetchedImageViewParmas.image; @@ -35,12 +40,17 @@ inline core::smart_refctd_ptr createScreenShot( if(!fetchedGpuImageParams.usage.hasFlags(IImage::EUF_TRANSFER_SRC_BIT)) { - assert(false); + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: source image missing TRANSFER_SRC usage.", system::ILogger::ELL_ERROR); return nullptr; } if (isBlockCompressionFormat(fetchedGpuImageParams.format)) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: block-compressed formats are not supported.", system::ILogger::ELL_ERROR); return nullptr; + } core::smart_refctd_ptr gpuTexelBuffer; @@ -48,10 +58,28 @@ inline core::smart_refctd_ptr createScreenShot( { // commandbuffer should refcount the pool, so it should be 100% legal to drop at the end of the scope auto gpuCommandPool = logicalDevice->createCommandPool(queue->getFamilyIndex(),IGPUCommandPool::CREATE_FLAGS::TRANSIENT_BIT); + if (!gpuCommandPool) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to create command pool.", system::ILogger::ELL_ERROR); + return nullptr; + } gpuCommandPool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY, 1u, &gpuCommandBuffer); - assert(gpuCommandBuffer); + if (!gpuCommandBuffer) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to create command buffer.", system::ILogger::ELL_ERROR); + return nullptr; + } + } + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: recording command buffer.", system::ILogger::ELL_INFO); + if (!gpuCommandBuffer->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT)) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to begin command buffer.", system::ILogger::ELL_ERROR); + return nullptr; } - gpuCommandBuffer->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT); { auto extent = gpuImage->getMipSize(); @@ -68,9 +96,27 @@ inline core::smart_refctd_ptr createScreenShot( bufferCreationParams.size = extent.x*extent.y*extent.z*getTexelOrBlockBytesize(fetchedGpuImageParams.format); bufferCreationParams.usage = IBuffer::EUF_TRANSFER_DST_BIT; gpuTexelBuffer = logicalDevice->createBuffer(std::move(bufferCreationParams)); + if (!gpuTexelBuffer) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to create GPU texel buffer.", system::ILogger::ELL_ERROR); + return nullptr; + } auto gpuTexelBufferMemReqs = gpuTexelBuffer->getMemoryReqs(); gpuTexelBufferMemReqs.memoryTypeBits &= logicalDevice->getPhysicalDevice()->getDownStreamingMemoryTypeBits(); + if (!gpuTexelBufferMemReqs.memoryTypeBits) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: no down-streaming memory type for texel buffer.", system::ILogger::ELL_ERROR); + return nullptr; + } auto gpuTexelBufferMem = logicalDevice->allocate(gpuTexelBufferMemReqs, gpuTexelBuffer.get()); + if (!gpuTexelBufferMem.isValid()) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to allocate texel buffer memory.", system::ILogger::ELL_ERROR); + return nullptr; + } IGPUCommandBuffer::SPipelineBarrierDependencyInfo info = {}; decltype(info)::image_barrier_t barrier = {}; @@ -102,7 +148,12 @@ inline core::smart_refctd_ptr createScreenShot( gpuCommandBuffer->pipelineBarrier(EDF_NONE,info); } } - gpuCommandBuffer->end(); + if (!gpuCommandBuffer->end()) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to end command buffer.", system::ILogger::ELL_ERROR); + return nullptr; + } auto signalSemaphore = logicalDevice->createSemaphore(0); @@ -124,22 +175,63 @@ inline core::smart_refctd_ptr createScreenShot( info.waitSemaphores = { &waitSemaphoreInfo, &waitSemaphoreInfo + 1 }; } - queue->submit({ &info, &info + 1}); + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: submitting copy command buffer.", system::ILogger::ELL_INFO); + if (queue->submit({ &info, &info + 1}) != IQueue::RESULT::SUCCESS) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to submit copy command buffer.", system::ILogger::ELL_ERROR); + return nullptr; + } ISemaphore::SWaitInfo waitInfo{ signalSemaphore.get(), 1u}; + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: waiting for copy completion.", system::ILogger::ELL_INFO); if (logicalDevice->blockForSemaphores({&waitInfo, &waitInfo + 1}) != ISemaphore::WAIT_RESULT::SUCCESS) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to wait for copy completion.", system::ILogger::ELL_ERROR); return nullptr; + } core::smart_refctd_ptr cpuImageView; { const auto gpuTexelBufferSize = gpuTexelBuffer->getSize(); // If you get validation errors from the `invalidateMappedMemoryRanges` we need to expose VK_WHOLE_BUFFER equivalent constant - ILogicalDevice::MappedMemoryRange mappedMemoryRange(gpuTexelBuffer->getBoundMemory().memory,0u,gpuTexelBufferSize); + auto* allocation = gpuTexelBuffer->getBoundMemory().memory; + if (!allocation) + return nullptr; - if (gpuTexelBuffer->getBoundMemory().memory->haveToMakeVisible()) + bool mappedHere = false; + if (!allocation->getMappedPointer()) + { + const IDeviceMemoryAllocation::MemoryRange range = { 0u, gpuTexelBufferSize }; + if (!allocation->map(range, IDeviceMemoryAllocation::EMCAF_READ)) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to map texel buffer memory.", system::ILogger::ELL_ERROR); + return nullptr; + } + mappedHere = true; + } + + ILogicalDevice::MappedMemoryRange mappedMemoryRange(allocation,0u,gpuTexelBufferSize); + if (allocation->haveToMakeVisible()) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: invalidating mapped range.", system::ILogger::ELL_INFO); logicalDevice->invalidateMappedMemoryRanges(1u,&mappedMemoryRange); + } auto cpuNewImage = ICPUImage::create(std::move(fetchedGpuImageParams)); + if (!cpuNewImage) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to create CPU image.", system::ILogger::ELL_ERROR); + if (mappedHere) + allocation->unmap(); + return nullptr; + } auto regions = core::make_refctd_dynamic_array>(1u); ICPUImage::SBufferCopy& region = regions->front(); @@ -155,10 +247,22 @@ inline core::smart_refctd_ptr createScreenShot( region.imageExtent = cpuNewImage->getCreationParameters().extent; auto cpuNewTexelBuffer = ICPUBuffer::create({ gpuTexelBufferSize }); + if (!cpuNewTexelBuffer) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: failed to create CPU buffer.", system::ILogger::ELL_ERROR); + if (mappedHere) + allocation->unmap(); + return nullptr; + } + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: copying GPU data to CPU buffer.", system::ILogger::ELL_INFO); { - memcpy(cpuNewTexelBuffer->getPointer(), gpuTexelBuffer->getBoundMemory().memory->getMappedPointer(), gpuTexelBuffer->getSize()); + memcpy(cpuNewTexelBuffer->getPointer(), allocation->getMappedPointer(), gpuTexelBuffer->getSize()); } cpuNewImage->setBufferAndRegions(core::smart_refctd_ptr(cpuNewTexelBuffer), regions); + if (mappedHere) + allocation->unmap(); { auto newCreationParams = cpuNewImage->getCreationParameters(); @@ -190,6 +294,12 @@ inline bool createScreenShot( { assert(outFile->getFlags()&system::IFile::ECF_WRITE); auto cpuImageView = createScreenShot(logicalDevice,queue,semaphore,gpuImageView,accessMask,imageLayout); + if (!cpuImageView) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: GPU readback failed, no image to write.", system::ILogger::ELL_ERROR); + return false; + } IAssetWriter::SAssetWriteParams writeParams(cpuImageView.get()); return assetManager->writeAsset(outFile,writeParams); } @@ -205,6 +315,12 @@ inline bool createScreenShot( const ACCESS_FLAGS accessMask = ACCESS_FLAGS::MEMORY_WRITE_BITS) { auto cpuImageView = createScreenShot(logicalDevice,queue,semaphore,gpuImageView,accessMask,imageLayout); + if (!cpuImageView) + { + if (auto* logger = logicalDevice->getLogger()) + logger->log("ScreenShot: GPU readback failed, no image to write.", system::ILogger::ELL_ERROR); + return false; + } IAssetWriter::SAssetWriteParams writeParams(cpuImageView.get()); return assetManager->writeAsset(filename.string(),writeParams); // TODO: Use std::filesystem::path } @@ -212,4 +328,4 @@ inline bool createScreenShot( } // namespace nbl::ext::ScreenShot -#endif \ No newline at end of file +#endif diff --git a/include/nbl/scene/ISkinInstanceCache.h b/include/nbl/scene/ISkinInstanceCache.h index 6cb18160d4..2eb83b4aac 100644 --- a/include/nbl/scene/ISkinInstanceCache.h +++ b/include/nbl/scene/ISkinInstanceCache.h @@ -19,7 +19,7 @@ class ISkinInstanceCache : public virtual core::IReferenceCounted // main pseudo-pool properties using joint_t = ITransformTree::node_t; - using skinning_matrix_t = core::matrix3x4SIMD; + using skinning_matrix_t = hlsl::float32_t3x4; using recomputed_stamp_t = ITransformTree::recomputed_stamp_t; using inverse_bind_pose_offset_t = uint32_t; @@ -35,7 +35,7 @@ class ISkinInstanceCache : public virtual core::IReferenceCounted static inline constexpr uint32_t inverse_bind_pose_offset_prop_ix = 3u; // for the inverse bind pose pool - using inverse_bind_pose_t = core::matrix3x4SIMD; + using inverse_bind_pose_t = hlsl::float32_t3x4; static inline constexpr uint32_t inverse_bind_pose_prop_ix = 0u; diff --git a/include/nbl/scene/ISkinInstanceCacheManager.h b/include/nbl/scene/ISkinInstanceCacheManager.h index 5a5e3f5881..474a8a3eaa 100644 --- a/include/nbl/scene/ISkinInstanceCacheManager.h +++ b/include/nbl/scene/ISkinInstanceCacheManager.h @@ -466,7 +466,7 @@ class ISkinInstanceCacheManager : public virtual core::IReferenceCounted } struct DebugPushConstants { - core::matrix4SIMD viewProjectionMatrix; + hlsl::float32_t4x4 viewProjectionMatrix; core::vector4df_SIMD lineColor; core::vector3df aabbColor; uint32_t skinCount; diff --git a/include/nbl/system/CStdoutLogger.h b/include/nbl/system/CStdoutLogger.h index 24693edd61..a63b8cf567 100644 --- a/include/nbl/system/CStdoutLogger.h +++ b/include/nbl/system/CStdoutLogger.h @@ -15,7 +15,7 @@ class CStdoutLogger : public IThreadsafeLogger protected: virtual void threadsafeLog_impl(const std::string_view& fmt, E_LOG_LEVEL logLevel, va_list args) override { - printf(constructLogString(fmt, logLevel, args).data()); + printf("%s", constructLogString(fmt, logLevel, args).data()); fflush(stdout); } diff --git a/include/nbl/system/IFileBase.h b/include/nbl/system/IFileBase.h index cb0170157e..c9ceb13a04 100644 --- a/include/nbl/system/IFileBase.h +++ b/include/nbl/system/IFileBase.h @@ -25,7 +25,9 @@ class IFileBase : public core::IReferenceCounted ECF_READ_WRITE = 0b0011, ECF_MAPPABLE = 0b0100, //! Implies ECF_MAPPABLE - ECF_COHERENT = 0b1100 + ECF_COHERENT = 0b1100, + ECF_SHARE_READ_WRITE = 0b100000, + ECF_SHARE_DELETE = 0b1000000 }; //! Get size of file. diff --git a/include/nbl/system/ISystem.h b/include/nbl/system/ISystem.h index 4e02221d7c..65f0351582 100644 --- a/include/nbl/system/ISystem.h +++ b/include/nbl/system/ISystem.h @@ -70,6 +70,7 @@ class NBL_API2 ISystem : public core::IReferenceCounted // virtual inline bool isDirectory(const system::path& p) const { + // TODO: fix bug, input "nbl/ext/DebugDraw/builtin/hlsl" -> returs true when no such dir present in mounted stuff due to how it uses parent paths in loop (goes up up till matches "nbl" builtin archive and thinks it resolved the requested dir) if (isPathReadOnly(p)) return p.extension()==""; // TODO: this is a temporary decision until we figure out how to check if a file is directory in android APK else diff --git a/include/nbl/system/declarations.h b/include/nbl/system/declarations.h index 2e66498a61..fa3dc2c6da 100644 --- a/include/nbl/system/declarations.h +++ b/include/nbl/system/declarations.h @@ -5,6 +5,7 @@ #define _NBL_SYSTEM_DECLARATIONS_H_INCLUDED_ #include "nbl/core/declarations.h" +#include "nbl/core/definitions.h" // basic stuff #include "nbl/system/DynamicLibraryFunctionPointer.h" @@ -13,6 +14,9 @@ #include "nbl/system/DynamicFunctionCaller.h" #include "nbl/system/SReadWriteSpinLock.h" +// printing and serialization +#include "nbl/system/to_string.h" + // files #include "nbl/system/IFile.h" diff --git a/include/nbl/system/to_string.h b/include/nbl/system/to_string.h new file mode 100644 index 0000000000..2a06ace5e5 --- /dev/null +++ b/include/nbl/system/to_string.h @@ -0,0 +1,100 @@ +#ifndef _NBL_SYSTEM_TO_STRING_INCLUDED_ +#define _NBL_SYSTEM_TO_STRING_INCLUDED_ + +#include +#include +#include + +namespace nbl +{ +namespace system +{ +namespace impl +{ + +template +struct to_string_helper +{ + static std::string __call(const T& value) + { + return std::to_string(value); + } +}; + +template<> +struct to_string_helper +{ + static std::string __call(const hlsl::emulated_uint64_t& value) + { + return std::to_string(static_cast(value)); + } +}; + +template<> +struct to_string_helper +{ + static std::string __call(const hlsl::emulated_int64_t& value) + { + return std::to_string(static_cast(value)); + } +}; + +template +struct to_string_helper> +{ + static std::string __call(const hlsl::vector& value) + { + std::stringstream output; + output << "{ "; + for (int i = 0; i < N; ++i) + { + output << to_string_helper::__call(value[i]); + + if (i < N - 1) + output << ", "; + } + output << " }"; + + return output.str(); + } +}; + +template +struct to_string_helper> +{ + static std::string __call(const hlsl::matrix& matrix) + { + std::stringstream output; + output << '\n'; + for (int i = 0; i < N; ++i) + { + output << "{ "; + for (int j = 0; j < M; ++j) + output << matrix[i][j] << ", "; + output << "}\n"; + } + return output.str(); + } +}; + +template +struct to_string_helper> +{ + using value_t = hlsl::morton::code; + static std::string __call(value_t value) + { + return to_string_helper::__call(value.value); + } +}; + +} + +template +std::string to_string(T value) +{ + return impl::to_string_helper::__call(value); +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/video/CVulkanCommon.h b/include/nbl/video/CVulkanCommon.h index 4232860baa..e4dfb7e3e9 100644 --- a/include/nbl/video/CVulkanCommon.h +++ b/include/nbl/video/CVulkanCommon.h @@ -1098,14 +1098,15 @@ inline VkPipelineBindPoint getVkPipelineBindPointFrom(asset::E_PIPELINE_BIND_POI } } -inline VkStridedDeviceAddressRegionKHR getVkStridedDeviceAddressRegion(const asset::SBufferRange& range, uint32_t stride) +inline VkStridedDeviceAddressRegionKHR getVkStridedDeviceAddressRegion(const asset::SStridedRange& stRange) { - if (range.buffer.get() == nullptr) + const auto& range = stRange.range; + if (range.buffer.get()==nullptr) return {}; return { .deviceAddress = range.buffer->getDeviceAddress() + range.offset, - .stride = stride, + .stride = stRange.stride, .size = range.size, }; } diff --git a/include/nbl/video/IGPUAccelerationStructure.h b/include/nbl/video/IGPUAccelerationStructure.h index 1bb4fb0c66..3c10a255a2 100644 --- a/include/nbl/video/IGPUAccelerationStructure.h +++ b/include/nbl/video/IGPUAccelerationStructure.h @@ -272,7 +272,7 @@ class IGPUBottomLevelAccelerationStructure : public asset::IBottomLevelAccelerat // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03809 // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03810 // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkBuildAccelerationStructuresKHR-pInfos-03773 - if (Base::invalidInputBuffer(geometry.transform,buildRangeInfo.transformByteOffset,1u,sizeof(core::matrix3x4SIMD),sizeof(core::vectorSIMDf))) + if (Base::invalidInputBuffer(geometry.transform,buildRangeInfo.transformByteOffset,1u,sizeof(hlsl::float32_t3x4),sizeof(core::vectorSIMDf))) return false; } else @@ -622,7 +622,7 @@ class IGPUTopLevelAccelerationStructure : public asset::ITopLevelAccelerationStr inline PolymorphicInstance(const PolymorphicInstance&) = default; inline PolymorphicInstance(PolymorphicInstance&&) = default; - // I made all these assignment operators because of the `core::matrix3x4SIMD` alignment and keeping `type` correct at all times + // I made all these assignment operators because of the `hlsl::float32_t3x4` alignment and keeping `type` correct at all times inline PolymorphicInstance& operator=(const StaticInstance& _static) { type = INSTANCE_TYPE::STATIC; @@ -657,7 +657,7 @@ class IGPUTopLevelAccelerationStructure : public asset::ITopLevelAccelerationStr static_assert(std::is_same_v,uint32_t>); // these must be 0 as per vulkan spec uint32_t reservedMotionFlags = 0u; - // I don't do an actual union because the preceeding members don't play nicely with alignment of `core::matrix3x4SIMD` and Vulkan requires this struct to be packed + // I don't do an actual union because the preceeding members don't play nicely with alignment of `hlsl::float32_t3x4` and Vulkan requires this struct to be packed SRTMotionInstance largestUnionMember = {}; static_assert(alignof(SRTMotionInstance)==8ull); diff --git a/include/nbl/video/IGPUCommandBuffer.h b/include/nbl/video/IGPUCommandBuffer.h index bb6460754a..6b3bfef18c 100644 --- a/include/nbl/video/IGPUCommandBuffer.h +++ b/include/nbl/video/IGPUCommandBuffer.h @@ -539,12 +539,8 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject bool resolveImage(const IGPUImage* const srcImage, const IGPUImage::LAYOUT srcImageLayout, IGPUImage* const dstImage, const IGPUImage::LAYOUT dstImageLayout, const uint32_t regionCount, const SImageResolve* const pRegions); bool setRayTracingPipelineStackSize(uint32_t pipelineStackSize); - bool traceRays( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth); + + bool traceRays(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth); bool traceRaysIndirect(const asset::SBufferBinding& indirectBinding); //! Secondary CommandBuffer execute @@ -719,14 +715,8 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject virtual bool resolveImage_impl(const IGPUImage* const srcImage, const IGPUImage::LAYOUT srcImageLayout, IGPUImage* const dstImage, const IGPUImage::LAYOUT dstImageLayout, const uint32_t regionCount, const SImageResolve* pRegions) = 0; virtual bool setRayTracingPipelineStackSize_impl(uint32_t pipelineStackSize) = 0; - virtual bool traceRays_impl( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth) = 0; - virtual bool traceRaysIndirect_impl( - const asset::SBufferBinding& indirectBinding) = 0; + virtual bool traceRays_impl(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth) = 0; + virtual bool traceRaysIndirect_impl(const asset::SBufferBinding& indirectBinding) = 0; virtual bool executeCommands_impl(const uint32_t count, IGPUCommandBuffer* const* const cmdbufs) = 0; @@ -881,14 +871,8 @@ class NBL_API2 IGPUCommandBuffer : public IBackendObject } return invalidImage(image,IGPUImage::EUF_TRANSFER_SRC_BIT); } - - bool invalidShaderGroups( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - core::bitflag flags) const; + bool invalidShaderGroups(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const core::bitflag flags) const; // returns total number of Geometries across all AS build infos template uint32_t buildAccelerationStructures_common(const std::span infos, BuildRangeInfos ranges, const IGPUBuffer* const indirectBuffer=nullptr); diff --git a/include/nbl/video/IGPUCommandPool.h b/include/nbl/video/IGPUCommandPool.h index 0424ad83bd..ddc4fcfd5c 100644 --- a/include/nbl/video/IGPUCommandPool.h +++ b/include/nbl/video/IGPUCommandPool.h @@ -71,7 +71,7 @@ class IGPUCommandPool : public IBackendObject m_commandListPool.clear(); return reset_impl(); } - inline uint32_t getResetCounter() { return m_resetCount.load(); } + inline uint64_t getResetCounter() { return m_resetCount.load(); } // recycles unused memory from the command pool back to the system virtual void trim() = 0; // no extra stuff needed for `CCommandSegmentListPool` because it trims unused blocks at runtime diff --git a/include/nbl/video/IGPURayTracingPipeline.h b/include/nbl/video/IGPURayTracingPipeline.h index 816cc68243..7b81ee43e7 100644 --- a/include/nbl/video/IGPURayTracingPipeline.h +++ b/include/nbl/video/IGPURayTracingPipeline.h @@ -10,9 +10,9 @@ namespace nbl::video { -class IGPURayTracingPipeline : public IGPUPipeline> +class IGPURayTracingPipeline : public IGPUPipeline> { - using pipeline_t = asset::IRayTracingPipeline; + using pipeline_t = asset::IRayTracingPipeline; public: struct SHitGroup @@ -172,8 +172,7 @@ class IGPURayTracingPipeline : public IGPUPipeline(params.layout->getOriginDevice()), params.layout, params.cached) - {} + IGPURayTracingPipeline(const SCreationParams& params) : IGPUPipeline(core::smart_refctd_ptr(params.layout->getOriginDevice()), params.layout, params.cached) {} virtual ~IGPURayTracingPipeline() = default; diff --git a/include/nbl/video/utilities/CDefaultSwapchainFramebuffers.h b/include/nbl/video/utilities/CDefaultSwapchainFramebuffers.h index 190fa81e70..285d7f46a7 100644 --- a/include/nbl/video/utilities/CDefaultSwapchainFramebuffers.h +++ b/include/nbl/video/utilities/CDefaultSwapchainFramebuffers.h @@ -17,7 +17,10 @@ namespace nbl::video class CDefaultSwapchainFramebuffers : public ISimpleManagedSurface::ISwapchainResources { public: - inline CDefaultSwapchainFramebuffers(ILogicalDevice* device, const asset::E_FORMAT format, const IGPURenderpass::SCreationParams::SSubpassDependency* dependencies) : m_device(device) + inline CDefaultSwapchainFramebuffers( + ILogicalDevice* device, const asset::E_FORMAT format, const IGPURenderpass::SCreationParams::SSubpassDependency* dependencies, + IGPURenderpass::LOAD_OP loadOp = IGPURenderpass::LOAD_OP::CLEAR + ) : m_device(device) { // If we create the framebuffers by default, we also need to default the renderpass (except dependencies) static const IGPURenderpass::SCreationParams::SColorAttachmentDescription colorAttachments[] = { @@ -27,7 +30,7 @@ class CDefaultSwapchainFramebuffers : public ISimpleManagedSurface::ISwapchainRe .samples = IGPUImage::E_SAMPLE_COUNT_FLAGS::ESCF_1_BIT, .mayAlias = false }, - /*.loadOp = */IGPURenderpass::LOAD_OP::CLEAR, + /*.loadOp = */loadOp, /*.storeOp = */IGPURenderpass::STORE_OP::STORE, /*.initialLayout = */IGPUImage::LAYOUT::UNDEFINED, // because we clear we don't care about contents /*.finalLayout = */ IGPUImage::LAYOUT::PRESENT_SRC // transition to presentation right away so we can skip a barrier @@ -51,6 +54,10 @@ class CDefaultSwapchainFramebuffers : public ISimpleManagedSurface::ISwapchainRe m_renderpass = m_device->createRenderpass(m_params); return m_renderpass.get(); } + inline const IGPURenderpass* getRenderpass() const + { + return m_renderpass.get(); + } inline IGPUFramebuffer* getFramebuffer(const uint8_t imageIx) { diff --git a/include/nbl/video/utilities/CSimpleResizeSurface.h b/include/nbl/video/utilities/CSimpleResizeSurface.h index 3e9abc6e25..126c9d179e 100644 --- a/include/nbl/video/utilities/CSimpleResizeSurface.h +++ b/include/nbl/video/utilities/CSimpleResizeSurface.h @@ -57,6 +57,7 @@ class CSimpleResizeSurface final : public ISimpleManagedSurface // Can be public because we don't need to worry about mutexes unlike the Smooth Resize class inline ISwapchainResources* getSwapchainResources() override {return m_swapchainResources.get();} + inline const ISwapchainResources* getSwapchainResources() const {return m_swapchainResources.get();} // need to see if the swapchain is invalidated (e.g. because we're starting from 0-area old Swapchain) and try to recreate the swapchain inline SAcquireResult acquireNextImage() diff --git a/include/nbl/video/utilities/ISimpleManagedSurface.h b/include/nbl/video/utilities/ISimpleManagedSurface.h index dbde2d5f53..f60aa022dd 100644 --- a/include/nbl/video/utilities/ISimpleManagedSurface.h +++ b/include/nbl/video/utilities/ISimpleManagedSurface.h @@ -186,6 +186,13 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted // inline bool irrecoverable() const {return !const_cast(this)->getSwapchainResources();} + // to trigger `becomeIrrecoverable` if window got closwd + inline bool isWindowOpen() + { + if (!m_cb) return true; // native hwnd has no callbacks set -> user's responsibility to not acquire on window close corresponding to the Surface HWND + return m_cb->isWindowOpen(); + } + // inline CThreadSafeQueueAdapter* getAssignedQueue() const {return m_queue;} @@ -339,13 +346,6 @@ class NBL_API2 ISimpleManagedSurface : public core::IReferenceCounted // virtual void deinit_impl() = 0; - // to trigger `becomeIrrecoverable` if window got closwd - inline bool isWindowOpen() - { - if (!m_cb) return true; // native hwnd has no callbacks set -> user's responsibility to not acquire on window close corresponding to the Surface HWND - return m_cb->isWindowOpen(); - } - // ICallback* const m_cb = nullptr; diff --git a/include/quaternion.h b/include/quaternion.h deleted file mode 100644 index c1867235db..0000000000 --- a/include/quaternion.h +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright (C) 2019 - 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_QUATERNION_H_INCLUDED__ -#define __NBL_QUATERNION_H_INCLUDED__ - - -#include "vectorSIMD.h" - -#include "nbl/core/math/glslFunctions.h" - - -namespace nbl -{ -namespace core -{ - -class matrix3x4SIMD; - - -//! Quaternion class for representing rotations. -/** It provides cheap combinations and avoids gimbal locks. -Also useful for interpolations. */ -class quaternion : private vectorSIMDf -{ - public: - //! Default Constructor - inline quaternion() : vectorSIMDf(0,0,0,1) {} - - inline quaternion(const quaternion& other) : vectorSIMDf(static_cast(other)) {} - - inline quaternion(const float* data) : vectorSIMDf(data) {} - - //! Constructor - inline quaternion(const float& x, const float& y, const float& z, const float& w) : vectorSIMDf(x,y,z,w) { } - - //! Constructor which converts euler angles (radians) to a quaternion - inline quaternion(const float& pitch, const float& yaw, const float& roll) {set(pitch,yaw,roll);} - - //! Constructor which converts a matrix to a quaternion - explicit quaternion(const matrix3x4SIMD& m); - - inline float* getPointer() {return pointer;} - - //! Equalilty operator - inline vector4db_SIMD operator==(const quaternion& other) const {return vectorSIMDf::operator==(other);} - - //! inequality operator - inline vector4db_SIMD operator!=(const quaternion& other) const {return vectorSIMDf::operator!=(other);} - - //! Assignment operator - inline quaternion& operator=(const quaternion& other) {return reinterpret_cast(vectorSIMDf::operator=(other));} - - //! Multiplication operator with scalar - inline quaternion operator*(const float& s) const - { - quaternion tmp; - reinterpret_cast(tmp) = reinterpret_cast(this)->operator*(s); - return tmp; - } - - //! Multiplication operator with scalar - inline quaternion& operator*=(const float& s) - { - *this = (*this)*s; - return *this; - } - - //! Multiplication operator - inline quaternion& operator*=(const quaternion& other) - { - *this = (*this)*other; - return *this; - } - - //! Multiplication operator - //http://momchil-velikov.blogspot.fr/2013/10/fast-sse-quternion-multiplication.html - inline quaternion operator*(const quaternion& other) const - { - __m128 xyzw = vectorSIMDf::getAsRegister(); - __m128 abcd = reinterpret_cast(other).getAsRegister(); - - __m128 t0 = FAST_FLOAT_SHUFFLE(abcd, _MM_SHUFFLE (3, 3, 3, 3)); /* 1, 0.5 */ - __m128 t1 = FAST_FLOAT_SHUFFLE(xyzw, _MM_SHUFFLE (2, 3, 0, 1)); /* 1, 0.5 */ - - __m128 t3 = FAST_FLOAT_SHUFFLE(abcd, _MM_SHUFFLE (0, 0, 0, 0)); /* 1, 0.5 */ - __m128 t4 = FAST_FLOAT_SHUFFLE(xyzw, _MM_SHUFFLE (1, 0, 3, 2)); /* 1, 0.5 */ - - __m128 t5 = FAST_FLOAT_SHUFFLE(abcd, _MM_SHUFFLE (1, 1, 1, 1)); /* 1, 0.5 */ - __m128 t6 = FAST_FLOAT_SHUFFLE(xyzw, _MM_SHUFFLE (2, 0, 3, 1)); /* 1, 0.5 */ - - /* [d,d,d,d]*[z,w,x,y] = [dz,dw,dx,dy] */ - __m128 m0 = _mm_mul_ps (t0, t1); /* 5/4, 1 */ - - /* [a,a,a,a]*[y,x,w,z] = [ay,ax,aw,az]*/ - __m128 m1 = _mm_mul_ps (t3, t4); /* 5/4, 1 */ - - /* [b,b,b,b]*[z,x,w,y] = [bz,bx,bw,by]*/ - __m128 m2 = _mm_mul_ps (t5, t6); /* 5/4, 1 */ - - /* [c,c,c,c]*[w,z,x,y] = [cw,cz,cx,cy] */ - __m128 t7 = FAST_FLOAT_SHUFFLE(abcd, _MM_SHUFFLE (2, 2, 2, 2)); /* 1, 0.5 */ - __m128 t8 = FAST_FLOAT_SHUFFLE(xyzw, _MM_SHUFFLE (3, 2, 0, 1)); /* 1, 0.5 */ - - __m128 m3 = _mm_mul_ps (t7, t8); /* 5/4, 1 */ - - /* 1 */ - /* [dz,dw,dx,dy]+-[ay,ax,aw,az] = [dz+ay,dw-ax,dx+aw,dy-az] */ - __m128 e = _mm_addsub_ps (m0, m1); /* 3, 1 */ - - /* 2 */ - /* [dx+aw,dz+ay,dy-az,dw-ax] */ - e = FAST_FLOAT_SHUFFLE(e, _MM_SHUFFLE (1, 3, 0, 2)); /* 1, 0.5 */ - - /* [dx+aw,dz+ay,dy-az,dw-ax]+-[bz,bx,bw,by] = [dx+aw+bz,dz+ay-bx,dy-az+bw,dw-ax-by]*/ - e = _mm_addsub_ps (e, m2); /* 3, 1 */ - - /* 2 */ - /* [dz+ay-bx,dw-ax-by,dy-az+bw,dx+aw+bz] */ - e = FAST_FLOAT_SHUFFLE(e, _MM_SHUFFLE (2, 0, 1, 3)); /* 1, 0.5 */ - - /* [dz+ay-bx,dw-ax-by,dy-az+bw,dx+aw+bz]+-[cw,cz,cx,cy] - = [dz+ay-bx+cw,dw-ax-by-cz,dy-az+bw+cx,dx+aw+bz-cy] */ - e = _mm_addsub_ps (e, m3); /* 3, 1 */ - - /* 2 */ - /* [dw-ax-by-cz,dz+ay-bx+cw,dy-az+bw+cx,dx+aw+bz-cy] */ - quaternion tmp; - reinterpret_cast(tmp) = FAST_FLOAT_SHUFFLE(e, _MM_SHUFFLE (2, 3, 1, 0)); /* 1, 0.5 */ - return tmp; - } - - inline vectorSIMDf transformVect(const vectorSIMDf& vec) - { - vectorSIMDf direction = *reinterpret_cast(this); - vectorSIMDf scale = core::length(direction); - direction.makeSafe3D(); - - return scale*vec+cross(direction,vec*W+cross(direction,vec))*2.f; - } - - //! Sets new quaternion - inline quaternion& set(const vectorSIMDf& xyzw) - { - *this = reinterpret_cast(xyzw); - return *this; - } - - //! Sets new quaternion based on euler angles (radians) - inline quaternion& set(const float& roll, const float& pitch, const float& yaw); - - //! Sets new quaternion from other quaternion - inline quaternion& set(const quaternion& quat) - { - *this = quat; - return *this; - } - - //! Inverts this quaternion - inline void makeInverse() - { - reinterpret_cast(*this) ^= _mm_set_epi32(0x0u,0x80000000u,0x80000000u,0x80000000u); - } - - //! Fills an angle (radians) around an axis (unit vector) - void toAngleAxis(float& angle, vector3df_SIMD& axis) const; - - //! Output this quaternion to an euler angle (radians) - void toEuler(vector3df_SIMD& euler) const; - - //! Set quaternion to identity - inline void makeIdentity() {vectorSIMDf::set(0,0,0,1);} - - - vectorSIMDf& getData() {return *((vectorSIMDf*)this);} - -//statics - inline static quaternion normalize(const quaternion& in) - { - quaternion tmp; - reinterpret_cast(tmp) = core::normalize(reinterpret_cast(in)); - return tmp; - } - - //! Helper func - static quaternion lerp(const quaternion &q1, const quaternion &q2, const float& interpolant, const bool& wrongDoubleCover); - - //! Set this quaternion to the linear interpolation between two quaternions - /** \param q1 First quaternion to be interpolated. - \param q2 Second quaternion to be interpolated. - \param interpolant Progress of interpolation. For interpolant=0 the result is - q1, for interpolant=1 the result is q2. Otherwise interpolation - between q1 and q2. - */ - static quaternion lerp(const quaternion &q1, const quaternion &q2, const float& interpolant); - - //! Helper func - static inline void flerp_interpolant_terms(float& interpolantPrecalcTerm2, float& interpolantPrecalcTerm3, const float& interpolant) - { - interpolantPrecalcTerm2 = (interpolant - 0.5f) * (interpolant - 0.5f); - interpolantPrecalcTerm3 = interpolant * (interpolant - 0.5f) * (interpolant - 1.f); - } - - static float flerp_adjustedinterpolant(const float& angle, const float& interpolant, const float& interpolantPrecalcTerm2, const float& interpolantPrecalcTerm3); - - //! Set this quaternion to the approximate slerp between two quaternions - /** \param q1 First quaternion to be interpolated. - \param q2 Second quaternion to be interpolated. - \param interpolant Progress of interpolation. For interpolant=0 the result is - q1, for interpolant=1 the result is q2. Otherwise interpolation - between q1 and q2. - */ - static quaternion flerp(const quaternion &q1, const quaternion &q2, const float& interpolant); - - //! Set this quaternion to the result of the spherical interpolation between two quaternions - /** \param q1 First quaternion to be interpolated. - \param q2 Second quaternion to be interpolated. - \param time Progress of interpolation. For interpolant=0 the result is - q1, for interpolant=1 the result is q2. Otherwise interpolation - between q1 and q2. - \param threshold To avoid inaccuracies the - interpolation switches to linear interpolation at some point. - This value defines how much of the interpolation will - be calculated with lerp. - */ - static quaternion slerp(const quaternion& q1, const quaternion& q2, - const float& interpolant, const float& threshold=.05f); - - inline static quaternion fromEuler(const vector3df_SIMD& euler) - { - quaternion tmp; - tmp.set(euler.X,euler.Y,euler.Z); - return tmp; - } - - inline static quaternion fromEuler(const vector3df& euler) - { - quaternion tmp; - tmp.set(euler.X,euler.Y,euler.Z); - return tmp; - } - - //! Set quaternion to represent a rotation from one vector to another. - static quaternion rotationFromTo(const vector3df_SIMD& from, const vector3df_SIMD& to); - - //! Create quaternion from rotation angle and rotation axis. - /** Axis must be unit length. - The quaternion representing the rotation is - q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k). - \param angle Rotation Angle in radians. - \param axis Rotation axis. */ - static quaternion fromAngleAxis(const float& angle, const vector3df_SIMD& axis); -}; -static_assert(sizeof(quaternion) == sizeof(vectorSIMDf), "Quaternion not same size as vec4"); - - -// set this quaternion to the result of the linear interpolation between two quaternions -inline quaternion quaternion::lerp(const quaternion &q1, const quaternion &q2, const float& interpolant, const bool& wrongDoubleCover) -{ - vectorSIMDf retval; - if (wrongDoubleCover) - retval = mix(reinterpret_cast(q1),-reinterpret_cast(q2),vectorSIMDf(interpolant)); - else - retval = mix(reinterpret_cast(q1), reinterpret_cast(q2),vectorSIMDf(interpolant)); - return reinterpret_cast(retval); -} - -// set this quaternion to the result of the linear interpolation between two quaternions -inline quaternion quaternion::lerp(const quaternion &q1, const quaternion &q2, const float& interpolant) -{ - const float angle = dot(q1,q2)[0]; - return lerp(q1,q2,interpolant,angle < 0.0f); -} - -// Arseny Kapoulkine -inline float quaternion::flerp_adjustedinterpolant(const float& angle, const float& interpolant, const float& interpolantPrecalcTerm2, const float& interpolantPrecalcTerm3) -{ - float A = 1.0904f + angle * (-3.2452f + angle * (3.55645f - angle * 1.43519f)); - float B = 0.848013f + angle * (-1.06021f + angle * 0.215638f); - float k = A * interpolantPrecalcTerm2 + B; - float ot = interpolant + interpolantPrecalcTerm3 * k; - return ot; -} - -// set this quaternion to the result of an approximate slerp -inline quaternion quaternion::flerp(const quaternion &q1, const quaternion &q2, const float& interpolant) -{ - const float angle = dot(q1,q2)[0]; - return lerp(q1,q2,flerp_adjustedinterpolant(fabsf(angle),interpolant,(interpolant - 0.5f) * (interpolant - 0.5f),interpolant * (interpolant - 0.5f) * (interpolant - 1.f)),angle < 0.0f); -} - - -// set this quaternion to the result of the interpolation between two quaternions -inline quaternion quaternion::slerp(const quaternion &q1, const quaternion &q2, const float& interpolant, const float& threshold) -{ - float angle = dot(q1,q2)[0]; - - // make sure we use the short rotation - bool wrongDoubleCover = angle < 0.0f; - if (wrongDoubleCover) - angle *= -1.f; - - if (angle <= (1.f-threshold)) // spherical interpolation - { // acosf + sinf - vectorSIMDf retval; - - const float sinARcp = inversesqrt(1.f-angle*angle); - const float sinAt = sinf(acosf(angle) * interpolant); // could this line be optimized? - //1sqrt 3min/add 5mul from now on - const float sinAt_over_sinA = sinAt*sinARcp; - - const float scale = core::sqrt(1.f-sinAt*sinAt)-angle*sinAt_over_sinA; //cosAt-cos(A)sin(tA)/sin(A) = (sin(A)cos(tA)-cos(A)sin(tA))/sin(A) - if (wrongDoubleCover) // make sure we use the short rotation - retval = reinterpret_cast(q1)*scale - reinterpret_cast(q2)*sinAt_over_sinA; - else - retval = reinterpret_cast(q1)*scale + reinterpret_cast(q2)*sinAt_over_sinA; - - return reinterpret_cast(retval); - } - else - return normalize(lerp(q1,q2,interpolant,wrongDoubleCover)); -} - - -#if !NBL_TEST_BROKEN_QUATERNION_USE -//! axis must be unit length, angle in radians -inline quaternion quaternion::fromAngleAxis(const float& angle, const vector3df_SIMD& axis) -{ - const float fHalfAngle = 0.5f*angle; - const float fSin = sinf(fHalfAngle); - quaternion retval; - reinterpret_cast(retval) = axis*fSin; - reinterpret_cast(retval).W = cosf(fHalfAngle); - return retval; -} - - -inline void quaternion::toAngleAxis(float& angle, vector3df_SIMD &axis) const -{ - vectorSIMDf scale = core::length(*reinterpret_cast(this)); - - if (scale.X==0.f) - { - angle = 0.0f; - axis.X = 0.0f; - axis.Y = 1.0f; - axis.Z = 0.0f; - } - else - { - axis = reinterpret_cast(this)->operator/(scale); - angle = 2.f * acosf(axis.W); - - axis.makeSafe3D(); - } -} - -inline void quaternion::toEuler(vector3df_SIMD& euler) const -{ - vectorSIMDf sqr = *reinterpret_cast(this); - sqr *= sqr; - const double test = 2.0 * (Y*W - X*Z); - - if (core::equals(test, 1.0, 0.000001)) - { - // heading = rotation about z-axis - euler.Z = (float) (-2.0*atan2(X, W)); - // bank = rotation about x-axis - euler.X = 0; - // attitude = rotation about y-axis - euler.Y = core::HALF_PI(); - } - else if (core::equals(test, -1.0, 0.000001)) - { - // heading = rotation about z-axis - euler.Z = (float) (2.0*atan2(X, W)); - // bank = rotation about x-axis - euler.X = 0; - // attitude = rotation about y-axis - euler.Y = -core::HALF_PI(); - } - else - { - // heading = rotation about z-axis - euler.Z = (float) atan2(2.0 * (X*Y +Z*W),(sqr.X - sqr.Y - sqr.Z + sqr.W)); - // bank = rotation about x-axis - euler.X = (float) atan2(2.0 * (Y*Z +X*W),(-sqr.X - sqr.Y + sqr.Z + sqr.W)); - // attitude = rotation about y-axis - euler.Y = (float) asin( core::clamp(test, -1.0, 1.0) ); - } -} - -inline quaternion quaternion::rotationFromTo(const vector3df_SIMD& from, const vector3df_SIMD& to) -{ - // Based on Stan Melax's article in Game Programming Gems - // Copy, since cannot modify local - vector3df_SIMD v0 = from; - vector3df_SIMD v1 = to; - v0 = core::normalize(v0); - v1 = core::normalize(v1); - - const vectorSIMDf dddd = core::dot(v0,v1); - quaternion tmp; - if (dddd.X >= 1.0f) // If dot == 1, vectors are the same - { - return tmp; - } - else if (dddd.X <= -1.0f) // exactly opposite - { - vector3df_SIMD axis(1.0f, 0.f, 0.f); - axis = cross(axis,v0); - if (length(axis)[0]==0.f) - { - axis.set(0.f,1.f,0.f); - axis = cross(axis,v0); - } - // same as fromAngleAxis(PI, axis).normalize(); - reinterpret_cast(tmp) = axis; - return normalize(tmp); - } - - vectorSIMDf s = core::sqrt(vectorSIMDf(2.f,2.f,2.f,0.f)+dddd*2.f); - reinterpret_cast(tmp) = cross(v0,v1)*reciprocal_approxim(s); - tmp.W = s.X*0.5f; - return normalize(tmp); -} -#endif - -// sets new quaternion based on euler angles -inline quaternion& quaternion::set(const float& roll, const float& pitch, const float& yaw) -{ - float angle; - - angle = roll * 0.5f; - const float sr = sinf(angle); - const float cr = cosf(angle); - - angle = pitch * 0.5f; - const float sp = sinf(angle); - const float cp = cos(angle); - - angle = yaw * 0.5f; - const float sy = sinf(angle); - const float cy = cosf(angle); - - const float cpcy = cp * cy; - const float spcy = sp * cy; - const float cpsy = cp * sy; - const float spsy = sp * sy; - - *reinterpret_cast(this) = vectorSIMDf(sr,cr,cr,cr)*vectorSIMDf(cpcy,spcy,cpsy,cpcy)+vectorSIMDf(-cr,sr,-sr,sr)*vectorSIMDf(spsy,cpsy,spcy,spsy); - - return *this; -} - -} // end namespace core -} // end namespace nbl - -#endif - diff --git a/include/shadertoolsconfig.json b/include/shadertoolsconfig.json new file mode 100644 index 0000000000..4c16b1f743 --- /dev/null +++ b/include/shadertoolsconfig.json @@ -0,0 +1,8 @@ +{ + "root": true, + "hlsl.preprocessorDefinitions": { + }, + "hlsl.additionalIncludeDirectories": [ + "." + ] +} \ No newline at end of file diff --git a/include/vectorSIMD.h b/include/vectorSIMD.h index 9b09f95c97..6144dc446f 100644 --- a/include/vectorSIMD.h +++ b/include/vectorSIMD.h @@ -887,6 +887,42 @@ namespace core } }; + // temporary solution until vectorSIMD gets deleted + inline hlsl::float32_t4 convertToHLSLVector(const vectorSIMDf& vec) + { + hlsl::float32_t4 retval; + retval.x = vec.x; + retval.y = vec.y; + retval.z = vec.z; + retval.w = vec.w; + + return retval; + } + + // temporary solution until vectorSIMD gets deleted + inline vectorSIMDf constructVecorSIMDFromHLSLVector(const hlsl::float32_t4& vec) + { + vectorSIMDf retval; + retval.x = vec.x; + retval.y = vec.y; + retval.z = vec.z; + retval.w = vec.w; + + return retval; + } + + // temporary solution until vectorSIMD gets deleted + inline vectorSIMDf constructVecorSIMDFromHLSLVector(const hlsl::float32_t3& vec) + { + vectorSIMDf retval; + retval.x = vec.x; + retval.y = vec.y; + retval.z = vec.z; + retval.w = 0.0f; + + return retval; + } + } // end namespace core } // end namespace nbl diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index 76e046848c..53e45edd22 100644 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -224,6 +224,11 @@ set(NBL_ASSET_SOURCES asset/interchange/CImageWriterTGA.cpp asset/interchange/CImageWriterOpenEXR.cpp # TODO: Nahim asset/interchange/CGLIWriter.cpp + +# IES profile loaders + asset/interchange/CIESProfileLoader.cpp + asset/utils/CIESProfileParser.cpp + asset/utils/CIESProfile.cpp ) set(NBL_VIDEO_SOURCES # Utilities @@ -394,6 +399,9 @@ nbl_adjust_flags(TARGET Nabla MAP_RELEASE Release MAP_RELWITHDEBINFO RelWithDebI nbl_adjust_definitions() option(NBL_WAVE_STRING_RESOLVER_TU_DEBUG_OPTIMISATION "Enable to optimise CWaveStringResolver.cpp in Debug configuration, uses RWDI compile options for the TU" ON) +if(NBL_SANITIZE_ADDRESS OR NBL_DEBUG_RTC_ENABLED) + set(NBL_WAVE_STRING_RESOLVER_TU_DEBUG_OPTIMISATION OFF CACHE BOOL "" FORCE) +endif() if(NBL_WAVE_STRING_RESOLVER_TU_DEBUG_OPTIMISATION) set_source_files_properties(asset/utils/CWaveStringResolver.cpp PROPERTIES # just enabling inlining and optimisations will help a lot @@ -689,6 +697,7 @@ if(NBL_EMBED_BUILTIN_RESOURCES) target_compile_definitions(${NBL_TARGET} PUBLIC "$") # workaround because must use Nabla headers without linking Nabla to itself.. target_include_directories(${NBL_TARGET} PUBLIC "$") target_include_directories(Nabla PRIVATE "${_INTERNAL_BR_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}") + target_include_directories(Nabla INTERFACE "$") endforeach() add_dependencies(Nabla ${NBL_TARGETS}) @@ -780,14 +789,6 @@ add_subdirectory(ext EXCLUDE_FROM_ALL) propagate_changed_variables_to_parent_scope() nbl_install_headers("${NABLA_HEADERS_PUBLIC}") -nbl_install_file_spec("${NBL_ROOT_PATH_BINARY}/include/nbl/builtin/builtinResources.h" "nbl/builtin") - -if(NBL_EMBED_BUILTIN_RESOURCES) - nbl_install_builtin_resources(nblBuiltinResourceData) - nbl_install_builtin_resources(spirvBuiltinResourceData) - nbl_install_builtin_resources(boostBuiltinResourceData) -endif() - set_target_properties(Nabla PROPERTIES DEBUG_POSTFIX _debug) set_target_properties(Nabla PROPERTIES RELWITHDEBINFO_POSTFIX _relwithdebinfo) @@ -875,4 +876,4 @@ source_group(TREE "${NBL_ROOT_PATH}" source_group(TREE "${NBL_ROOT_PATH}" PREFIX "Source Files" FILES ${NABLA_SOURCE_FILES} -) \ No newline at end of file +) diff --git a/src/nbl/asset/IAssetManager.cpp b/src/nbl/asset/IAssetManager.cpp index dc67ed8d01..29930bccd9 100644 --- a/src/nbl/asset/IAssetManager.cpp +++ b/src/nbl/asset/IAssetManager.cpp @@ -84,7 +84,7 @@ #endif #include "nbl/asset/interchange/CBufferLoaderBIN.h" -//#include "nbl/asset/interchange/CIESProfileLoader.h" +#include "nbl/asset/interchange/CIESProfileLoader.h" #include "nbl/asset/utils/CGeometryCreator.h" @@ -181,7 +181,7 @@ void IAssetManager::addLoadersAndWriters() #ifdef _NBL_COMPILE_WITH_GLI_WRITER_ addAssetWriter(core::make_smart_refctd_ptr(core::smart_refctd_ptr(m_system))); #endif -// addAssetLoader(core::make_smart_refctd_ptr()); +addAssetLoader(core::make_smart_refctd_ptr()); for (auto& loader : m_loaders.vector) loader->initialize(); @@ -236,14 +236,14 @@ SAssetBundle IAssetManager::getAssetInHierarchy_impl(system::IFile* _file, const ((levelFlags & IAssetLoader::ECF_DONT_CACHE_TOP_LEVEL) != IAssetLoader::ECF_DONT_CACHE_TOP_LEVEL) && ((levelFlags & IAssetLoader::ECF_DUPLICATE_TOP_LEVEL) != IAssetLoader::ECF_DUPLICATE_TOP_LEVEL)) { - _override->insertAssetIntoCache(bundle, filename.string(), ctx, _hierarchyLevel); + _override->insertAssetIntoCache(bundle, filename.string(), ctx.params, _hierarchyLevel); } else if (bundle.getContents().empty()) { bool addToCache; bundle = _override->handleLoadFail(addToCache, file.get(), filename.string(), filename.string(), ctx, _hierarchyLevel); if (!bundle.getContents().empty() && addToCache) - _override->insertAssetIntoCache(bundle, filename.string(), ctx, _hierarchyLevel); + _override->insertAssetIntoCache(bundle, filename.string(), ctx.params, _hierarchyLevel); } return bundle; } diff --git a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp index d4b9a3e394..b538f75eb3 100644 --- a/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp +++ b/src/nbl/asset/interchange/CGraphicsPipelineLoaderMTL.cpp @@ -894,7 +894,7 @@ auto CGraphicsPipelineLoaderMTL::readMaterials(system::IFile* _file, const syste case 'f': // Tf - Transmitivity currMaterial->params.transmissionFilter = readRGB(); sprintf(tmpbuf, "%s, %s: Detected Tf parameter, it won't be used in generated shader - fallback to alpha=0.5 instead", _file->getFileName().string().c_str(), currMaterial->name.c_str()); - logger.log(tmpbuf, system::ILogger::ELL_WARNING); + logger.log("%s", system::ILogger::ELL_WARNING, tmpbuf); break; case 'r': // Tr, transparency = 1.0-d currMaterial->params.opacity = (1.f - readFloat()); diff --git a/src/nbl/asset/interchange/CIESProfileLoader.cpp b/src/nbl/asset/interchange/CIESProfileLoader.cpp index eecdde3190..1bf60be905 100644 --- a/src/nbl/asset/interchange/CIESProfileLoader.cpp +++ b/src/nbl/asset/interchange/CIESProfileLoader.cpp @@ -3,24 +3,52 @@ using namespace nbl; using namespace asset; -asset::SAssetBundle -CIESProfileLoader::loadAsset(io::IReadFile* _file, - const asset::IAssetLoader::SAssetLoadParams& _params, - asset::IAssetLoader::IAssetLoaderOverride* _override, - uint32_t _hierarchyLevel) { - if (!_file) +bool CIESProfileLoader::isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const +{ + system::IFile::success_t success; + std::string versionBuffer(0x45, ' '); + const auto* fName = _file->getFileName().c_str(); + _file->read(success, versionBuffer.data(), 0, versionBuffer.size()); + + if (success) + { + for (const auto& it : CIESProfileParser::VALID_SIGNATURES) + if (versionBuffer.find(it.data()) != std::string::npos) + return true; + logger.log("%s: Invalid IES signature for \"%s\" file!", system::ILogger::ELL_DEBUG, __FUNCTION__, fName); + } + else + logger.log("%s: Failed to read \"%s\" file!", system::ILogger::ELL_DEBUG, __FUNCTION__, fName); + + return false; +} + +asset::SAssetBundle CIESProfileLoader::loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel) +{ + if (not _file) + { + _params.logger.log("%s: Nullptr system::IFile pointer!", system::ILogger::ELL_ERROR, __FUNCTION__); return {}; + } IAssetLoader::SAssetLoadContext loadContex(_params, _file); core::vector data(_file->getSize()); - _file->read(data.data(), _file->getSize()); + system::IFile::success_t success; + const auto* fName = _file->getFileName().c_str(); + _file->read(success, data.data(), 0, _file->getSize()); + + if (not success) + { + _params.logger.log("%s: Failed to read \"%s\" file!", system::ILogger::ELL_ERROR, __FUNCTION__, fName); + return {}; + } CIESProfileParser parser(data.data(), data.size()); CIESProfile profile; - if (!parser.parse(profile)) + if (not parser.parse(profile)) { - os::Printer::log("ERROR: Emission profile parsing error: " + std::string(parser.getErrorMsg()), ELL_ERROR); + _params.logger.log("%s: Failed to parse emission profile for \"%s\" file!", system::ILogger::ELL_ERROR, __FUNCTION__, fName); return {}; } @@ -31,8 +59,8 @@ CIESProfileLoader::loadAsset(io::IReadFile* _file, cpuImageView = _override->findDefaultAsset("nbl/builtin/image_view/dummy2d", loadContex, _hierarchyLevel).first; // note: we could also pass empty content, but this would require adjusting IAssetLoader source to not attempt to use all loaders to find the asset else { - const auto optimalResolution = profile.getOptimalIESResolution(); - cpuImageView = profile.createIESTexture(0.f, false, optimalResolution.x, optimalResolution.y); + const auto optimalResolution = profile.getAccessor().properties.optimalIESResolution; + cpuImageView = profile.createIESTexture(optimalResolution); } return asset::SAssetBundle(std::move(meta), { core::smart_refctd_ptr(cpuImageView) }); diff --git a/src/nbl/asset/interchange/CIESProfileLoader.h b/src/nbl/asset/interchange/CIESProfileLoader.h index 64ef9688ee..ba89915278 100644 --- a/src/nbl/asset/interchange/CIESProfileLoader.h +++ b/src/nbl/asset/interchange/CIESProfileLoader.h @@ -1,16 +1,15 @@ // 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_C_IES_PROFILE_LOADER_H_INCLUDED__ -#define __NBL_ASSET_C_IES_PROFILE_LOADER_H_INCLUDED__ +#ifndef _NBL_ASSET_C_IES_PROFILE_LOADER_H_INCLUDED_ +#define _NBL_ASSET_C_IES_PROFILE_LOADER_H_INCLUDED_ #include "nbl/asset/ICPUImage.h" -#include "nbl/asset/ICPUShader.h" #include "nbl/asset/IAssetManager.h" - #include "nbl/asset/interchange/IAssetLoader.h" -#include "nbl/asset/utils/CIESProfileParser.h" + +#include "nbl/asset/utils/CIESProfileParser.h" // TODO: move to `src/asset/interchange` #include "nbl/asset/metadata/CIESProfileMetadata.h" namespace nbl::asset @@ -25,20 +24,7 @@ class CIESProfileLoader final : public asset::IAssetLoader \return True if file seems to be loadable. */ - bool isALoadableFileFormat(io::IReadFile *_file) const override - { - const size_t begginingOfFile = _file->getPos(); - _file->seek(0ull); - std::string versionBuffer(0x45, ' '); - _file->read(versionBuffer.data(), versionBuffer.size()); - _file->seek(begginingOfFile); - - for (const auto& it : CIESProfileParser::VALID_SIGNATURES) - if (versionBuffer.find(it.data()) != std::string::npos) - return true; - - return false; - } + bool isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const override; //! Returns an array of string literals terminated by nullptr const char **getAssociatedFileExtensions() const override @@ -55,11 +41,8 @@ class CIESProfileLoader final : public asset::IAssetLoader uint64_t getSupportedAssetTypesBitfield() const override { return asset::IAsset::ET_IMAGE_VIEW; } //! Loads an asset from an opened file, returns nullptr in case of failure. - asset::SAssetBundle - loadAsset(io::IReadFile* _file, - const asset::IAssetLoader::SAssetLoadParams& _params, - asset::IAssetLoader::IAssetLoaderOverride* _override = nullptr, - uint32_t _hierarchyLevel = 0u) override; + asset::SAssetBundle loadAsset(system::IFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override = nullptr, uint32_t _hierarchyLevel = 0u) override; }; } // namespace nbl::asset + #endif // __NBL_ASSET_C_IES_PROFILE_LOADER_H_INCLUDED__ diff --git a/src/nbl/asset/interchange/CImageLoaderJPG.cpp b/src/nbl/asset/interchange/CImageLoaderJPG.cpp index 45677ff5cf..1db5e16ac2 100644 --- a/src/nbl/asset/interchange/CImageLoaderJPG.cpp +++ b/src/nbl/asset/interchange/CImageLoaderJPG.cpp @@ -93,7 +93,7 @@ namespace jpeg std::string errMsg("JPEG FATAL ERROR in "); auto ctx = reinterpret_cast(cinfo->client_data); errMsg += ctx->filename; - ctx->logger.log(errMsg + temp1, system::ILogger::ELL_ERROR); + ctx->logger.log("%s", system::ILogger::ELL_ERROR, errMsg + temp1); } /* Initialize source. This is called by jpeg_read_header() before any diff --git a/src/nbl/asset/interchange/IAssetLoader.cpp b/src/nbl/asset/interchange/IAssetLoader.cpp index edec446747..9a881b300b 100644 --- a/src/nbl/asset/interchange/IAssetLoader.cpp +++ b/src/nbl/asset/interchange/IAssetLoader.cpp @@ -6,8 +6,8 @@ #include "nbl/asset/IAssetManager.h" -using namespace nbl; -using namespace asset; +using namespace nbl::core; +using namespace nbl::asset; // todo NEED DOCS IAssetLoader::IAssetLoaderOverride::IAssetLoaderOverride(IAssetManager* _manager) : m_manager(_manager), m_system(m_manager->getSystem()) @@ -26,28 +26,28 @@ SAssetBundle IAssetLoader::IAssetLoaderOverride::findCachedAsset(const std::stri return chooseRelevantFromFound(found->begin(), found->end(), ctx, hierarchyLevel); } -void IAssetLoader::IAssetLoaderOverride::insertAssetIntoCache(SAssetBundle& asset, const std::string& supposedKey, const SAssetLoadContext& ctx, const uint32_t hierarchyLevel) +void IAssetLoader::IAssetLoaderOverride::insertAssetIntoCache(SAssetBundle& asset, const std::string& supposedKey, const SAssetLoadParams& _params, const uint32_t hierarchyLevel) { m_manager->changeAssetKey(asset, supposedKey); - auto levelFlag = ctx.params.cacheFlags >> (uint64_t(hierarchyLevel) * 2ull); + auto levelFlag = _params.cacheFlags >> (uint64_t(hierarchyLevel) * 2ull); if (!(levelFlag&ECF_DONT_CACHE_TOP_LEVEL)) m_manager->insertAssetIntoCache(asset,ASSET_MUTABILITY_ON_CACHE_INSERT); } -SAssetBundle IAssetLoader::interm_getAssetInHierarchy(IAssetManager* _mgr, system::IFile* _file, const std::string& _supposedFilename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) +SAssetBundle IAssetLoader::interm_getAssetInHierarchy(system::IFile* _file, const std::string& _supposedFilename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) { - return _mgr->getAssetInHierarchy(_file, _supposedFilename, _params, _hierarchyLevel, _override); + return _override->getManager()->getAssetInHierarchy(_file, _supposedFilename, _params, _hierarchyLevel, _override); } -SAssetBundle IAssetLoader::interm_getAssetInHierarchy(IAssetManager* _mgr, const std::string& _filename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) +SAssetBundle IAssetLoader::interm_getAssetInHierarchy(const std::string& _filename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) { - return _mgr->getAssetInHierarchy(_filename, _params, _hierarchyLevel, _override); + return _override->getManager()->getAssetInHierarchy(_filename, _params, _hierarchyLevel, _override); } -SAssetBundle IAssetLoader::interm_getAssetInHierarchyWithAllContent(IAssetManager* _mgr, const std::string& _filename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) +SAssetBundle IAssetLoader::interm_getAssetInHierarchyWithAllContent(const std::string& _filename, const IAssetLoader::SAssetLoadParams& _params, uint32_t _hierarchyLevel, IAssetLoader::IAssetLoaderOverride* _override) { - auto firstLoad = interm_getAssetInHierarchy(_mgr,_filename,_params,_hierarchyLevel,_override); + auto firstLoad = interm_getAssetInHierarchy(_filename,_params,_hierarchyLevel,_override); auto bundleHasAllContent = [](const SAssetBundle& retval)->bool { for (const auto& asset : retval.getContents()) @@ -60,10 +60,10 @@ SAssetBundle IAssetLoader::interm_getAssetInHierarchyWithAllContent(IAssetManage IAssetLoader::SAssetLoadParams paramCopy = _params; paramCopy.cacheFlags = ECF_DUPLICATE_REFERENCES; - auto secondLoad = interm_getAssetInHierarchy(_mgr,_filename,paramCopy,_hierarchyLevel,_override); + auto secondLoad = interm_getAssetInHierarchy(_filename,paramCopy,_hierarchyLevel,_override); if (bundleHasAllContent(secondLoad)) { - _mgr->removeAssetFromCache(firstLoad); + _override->getManager()->removeAssetFromCache(firstLoad); return secondLoad; } else @@ -83,15 +83,47 @@ bool IAssetLoader::insertBuiltinAssetIntoCache(IAssetManager* _mgr, SAssetBundle - -bool IAssetLoader::insertBuiltinAssetIntoCache(IAssetManager* _mgr, core::smart_refctd_ptr& _asset, core::smart_refctd_ptr&& metadata, const std::string _path) +// if I can figure out the template for this, move to header +bool IAssetLoader::insertBuiltinAssetIntoCache(IAssetManager* _mgr, smart_refctd_ptr& _asset, smart_refctd_ptr&& metadata, const std::string _path) { asset::SAssetBundle bundle(std::move(metadata), { _asset }); return insertBuiltinAssetIntoCache(_mgr, bundle, _path); } -bool IAssetLoader::insertBuiltinAssetIntoCache(IAssetManager* _mgr, core::smart_refctd_ptr&& _asset, core::smart_refctd_ptr&& metadata, const std::string _path) +bool IAssetLoader::insertBuiltinAssetIntoCache(IAssetManager* _mgr, smart_refctd_ptr&& _asset, smart_refctd_ptr&& metadata, const std::string _path) { asset::SAssetBundle bundle(std::move(metadata), { std::move(_asset) }); return insertBuiltinAssetIntoCache(_mgr, bundle, _path); } + + +smart_refctd_ptr IAssetLoader::createDefaultImageView(core::smart_refctd_ptr&& image) +{ + if (!image) + return nullptr; + const auto& imageParams = image->getCreationParameters(); + + using view_type_e = IImageViewBase::E_TYPE; + IImageViewBase::E_TYPE viewType; + switch (imageParams.type) + { + case ICPUImage::ET_1D: + viewType = view_type_e::ET_1D_ARRAY; + break; + case ICPUImage::ET_2D: + viewType = view_type_e::ET_2D_ARRAY; + break; + case ICPUImage::ET_3D: + viewType = view_type_e::ET_3D; + break; + default: + return nullptr; + } + + return ICPUImageView::create({ + //.subUsages = // shall we somehow narrow in-case the image itself has extended usage? + .image = std::move(image), + .viewType = viewType, + .format = imageParams.format + }); +} \ No newline at end of file diff --git a/src/nbl/asset/utils/CGeometryCreator.cpp b/src/nbl/asset/utils/CGeometryCreator.cpp index 2aa2e08fe5..3750a37a70 100644 --- a/src/nbl/asset/utils/CGeometryCreator.cpp +++ b/src/nbl/asset/utils/CGeometryCreator.cpp @@ -6,6 +6,7 @@ #include "nbl/asset/utils/CGeometryCreator.h" #include "nbl/builtin/hlsl/tgmath.hlsl" #include "nbl/builtin/hlsl/math/linalg/transform.hlsl" +#include "nbl/builtin/hlsl/math/quaternions.hlsl" #include #include @@ -746,7 +747,8 @@ core::smart_refctd_ptr CGeometryCreator::createArrow( geometries->push_back({ .geometry = cylinder }); - const auto coneTransform = hlsl::math::linalg::rotation_mat(hlsl::numbers::pi * -0.5f, hlsl::float32_t3(1.f, 0.f, 0.f)); + const auto coneRotation = hlsl::math::quaternion::create(hlsl::float32_t3(1.f, 0.f, 0.f), hlsl::numbers::pi * -0.5f); + const auto coneTransform = hlsl::math::linalg::promote_affine<3, 4>(hlsl::_static_cast(coneRotation)); geometries->push_back({ .transform = hlsl::math::linalg::promote_affine<3, 4>(coneTransform), .geometry = cone @@ -1888,5 +1890,132 @@ core::smart_refctd_ptr CGeometryCreator::createIcoSphere(fl return retval; } +core::smart_refctd_ptr CGeometryCreator::createGrid(const hlsl::uint16_t2 resolution) const +{ + using namespace hlsl; + + if (resolution.x < 2 || resolution.y < 2) + return nullptr; + + auto retval = core::make_smart_refctd_ptr(); + retval->setIndexing(IPolygonGeometryBase::TriangleStrip()); + + //! Create indices + /* + i \in [0, resolution.x - 1], j \in [0, resolution.y - 1] + logical vertex id : V(i, j) + + Eg. resolution = {5u, 4u}: + + j=3 15--16--17--18--19 + | \ | \ | \ | \ | + j=2 10--11--12--13--14 + | \ | \ | \ | \ | + j=1 5-- 6-- 7-- 8-- 9 + | \ | \ | \ | \ | + j=0 0-- 1-- 2-- 3-- 4 + i=0 1 2 3 4 + + Strip order (one draw), rows linked by 2 degenerate indices: + row 0 -> 1 (L->R): 0,5, 1,6, 2,7, 3,8, 4,9, 9,9 + row 1 -> 2 (R->L): 9,14, 8,13, 7,12, 6,11, 5,10, 5,5 + row 2 -> 3 (L->R): 5,10, 6,11, 7,12, 8,13, 9,14, 14,14 + */ + + const size_t indexCount = 2ull * resolution.x * (resolution.y - 1) + 2ull * (resolution.y - 2); + const size_t maxIndex = resolution.x * resolution.y - 1u; + + auto createIndices = [&]() -> void + { + auto indexView = createIndexView(indexCount, maxIndex); + + auto V = [&](IndexT i, IndexT j) { return IndexT(j * resolution.x + i); }; + auto* index = static_cast(indexView.src.buffer->getPointer()); + #define PUSH_INDEX(value) *index = value; ++index; + + for (IndexT j = 0u; j < resolution.y - 1; ++j) + { + if ((j & 1u) == 0) + { + for (IndexT i = 0u; i < resolution.x; ++i) + { + PUSH_INDEX(V(i, j)) + PUSH_INDEX(V(i, j + 1)) + } + + if (j + 1 < resolution.y - 1) + { + IndexT last = V(resolution.x - 1, j + 1); + PUSH_INDEX(last) + PUSH_INDEX(last) + } + } + else + { + for (int i = int(resolution.x) - 1; i >= 0; --i) + { + PUSH_INDEX(V(uint32_t(i), j)) + PUSH_INDEX(V(uint32_t(i), j + 1)) + } + + if (j + 1 < resolution.y - 1) + { + IndexT first = V(0, j + 1); + PUSH_INDEX(first) + PUSH_INDEX(first) + } + } + } + retval->setIndexView(std::move(indexView)); + }; + + if (maxIndex <= std::numeric_limits::max()) + createIndices.template operator() < uint16_t > (); + else if (maxIndex <= std::numeric_limits::max()) + createIndices.template operator() < uint32_t > (); + else + return nullptr; + + //! Create positions + const size_t vertexCount = resolution.x * resolution.y; + { + shapes::AABB<4, float32_t> aabb; + aabb.maxVx = float32_t4((resolution.x - 0.5f) / float(resolution.x), 0.5f, (resolution.y - 0.5f) / float(resolution.y), 1.f); + aabb.minVx = float32_t4(0.5f / float(resolution.x), 0.5f, 0.5f / float(resolution.y), 1.f); + + static constexpr auto stride = getTexelOrBlockBytesize(); + const auto bytes = stride * vertexCount; + auto buffer = ICPUBuffer::create({ bytes, IBuffer::EUF_NONE }); + ICPUPolygonGeometry::SDataView positionView = { + .composed = { + .encodedDataRange = {.f32 = aabb}, + .stride = stride, + .format = EF_A2R10G10B10_UNORM_PACK32, + .rangeFormat = IGeometryBase::EAABBFormat::F32 + }, + .src = {.offset = 0,.size = buffer->getSize(),.buffer = core::smart_refctd_ptr(buffer)} + }; + + auto* packed = reinterpret_cast(buffer->getPointer()); + for (uint32_t j = 0; j < resolution.y; ++j) + for (uint32_t i = 0; i < resolution.x; ++i) + { + const double u = (i + 0.5) / double(resolution.x); + const double v = (j + 0.5) / double(resolution.y); + + float64_t4 rgbaunorm = { u, 0.5, v, 1.0 }; + + *packed = {}; + encodePixels(packed, (double*)&rgbaunorm); + ++packed; + } + + retval->setPositionView(std::move(positionView)); + } + + CPolygonGeometryManipulator::recomputeContentHashes(retval.get()); + return retval; +} + } // end namespace nbl::asset diff --git a/src/nbl/asset/utils/CHLSLCompiler.cpp b/src/nbl/asset/utils/CHLSLCompiler.cpp index 306d2f60de..62166a885c 100644 --- a/src/nbl/asset/utils/CHLSLCompiler.cpp +++ b/src/nbl/asset/utils/CHLSLCompiler.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -111,15 +112,26 @@ static bool fixup_spirv_target_ver(std::vector& arguments, system: for (auto targetEnvArgumentPos=arguments.begin(); targetEnvArgumentPos!=arguments.end(); targetEnvArgumentPos++) if (targetEnvArgumentPos->find(Prefix)==0) { - const auto suffix = targetEnvArgumentPos->substr(Prefix.length()); + auto suffix = targetEnvArgumentPos->substr(Prefix.length()); + auto trim = [](std::wstring& value) { + auto isTrimChar = [](wchar_t c) { + return c == L' ' || c == L'\t' || c == L'\r' || c == L'\n' || c == L'"'; + }; + while (!value.empty() && isTrimChar(value.front())) + value.erase(value.begin()); + while (!value.empty() && isTrimChar(value.back())) + value.pop_back(); + }; + trim(suffix); const auto found = AllowedSuffices.find(suffix); if (found!=AllowedSuffices.end()) return true; - logger.log("Compile flag error: Required compile flag not found -fspv-target-env=. Force enabling -fspv-target-env= found but with unsupported value `%s`.", system::ILogger::ELL_ERROR, "TODO: write wchar to char convert usage"); - return false; + logger.log("Compile flag warning: Required compile flag not found -fspv-target-env=. Force enabling -fspv-target-env=vulkan1.3, previous value `%ls` is unsupported.", system::ILogger::ELL_WARNING, suffix.c_str()); + *targetEnvArgumentPos = L"-fspv-target-env=vulkan1.3"; + return true; } - logger.log("Compile flag error: Required compile flag not found -fspv-target-env=. Force enabling -fspv-target-env=vulkan1.3, as it is required by Nabla.", system::ILogger::ELL_WARNING); + logger.log("Compile flag warning: Required compile flag not found -fspv-target-env=. Force enabling -fspv-target-env=vulkan1.3, as it is required by Nabla.", system::ILogger::ELL_WARNING); arguments.push_back(L"-fspv-target-env=vulkan1.3"); return true; } @@ -148,7 +160,7 @@ static void try_upgrade_hlsl_version(std::vector& arguments, syste } else { - logger.log("Compile flag error: Required compile flag not found -HV. Force enabling -HV 202x, as it is required by Nabla.", system::ILogger::ELL_WARNING); + logger.log("Compile flag warning: Required compile flag not found -HV. Force enabling -HV 202x, as it is required by Nabla.", system::ILogger::ELL_WARNING); arguments.push_back(L"-HV"); arguments.push_back(L"202x"); } @@ -254,7 +266,7 @@ static void add_required_arguments_if_not_present(std::vector& arg { bool missing = set.find(required[j]) == set.end(); if (missing) { - logger.log("Compile flag error: Required compile flag not found %ls. This flag will be force enabled, as it is required by Nabla.", system::ILogger::ELL_WARNING, required[j]); + logger.log("Compile flag warning: Required compile flag not found %ls. This flag will be force enabled, as it is required by Nabla.", system::ILogger::ELL_WARNING, required[j]); arguments.push_back(required[j]); } } @@ -301,7 +313,7 @@ static DxcCompilationResult dxcCompile(const CHLSLCompiler* compiler, nbl::asset DxcBuffer sourceBuffer; sourceBuffer.Ptr = src->GetBufferPointer(); sourceBuffer.Size = src->GetBufferSize(); - sourceBuffer.Encoding = 0; + sourceBuffer.Encoding = CP_UTF8; ComPtr compileResult; res = dxc->m_dxcCompiler->Compile(&sourceBuffer, args, argCount, nullptr, IID_PPV_ARGS(compileResult.GetAddressOf())); @@ -353,6 +365,21 @@ namespace nbl::wave std::string CHLSLCompiler::preprocessShader(std::string&& code, IShader::E_SHADER_STAGE& stage, const SPreprocessorOptions& preprocessOptions, std::vector& dxc_compile_flags_override, std::vector* dependencies) const { + const bool depfileEnabled = preprocessOptions.depfile; + if (depfileEnabled) + { + if (preprocessOptions.depfilePath.empty()) + { + preprocessOptions.logger.log("Depfile path is empty.", system::ILogger::ELL_ERROR); + return {}; + } + } + + std::vector localDependencies; + auto* dependenciesOut = dependencies; + if (depfileEnabled && !dependenciesOut) + dependenciesOut = &localDependencies; + // HACK: we do a pre-pre-process here to add \n after every #pragma to neutralize boost::wave's actions // See https://github.com/Devsh-Graphics-Programming/Nabla/issues/746 size_t line_index = 0; @@ -367,8 +394,8 @@ std::string CHLSLCompiler::preprocessShader(std::string&& code, IShader::E_SHADE } // preprocess - core::string resolvedString = nbl::wave::preprocess(code, preprocessOptions, bool(dependencies) /* if dependencies were passed, we assume we want caching*/, - [&dxc_compile_flags_override, &stage, &dependencies](nbl::wave::context& context) -> void + core::string resolvedString = nbl::wave::preprocess(code, preprocessOptions, bool(dependenciesOut), + [&dxc_compile_flags_override, &stage, &dependenciesOut](nbl::wave::context& context) -> void { if (context.get_hooks().m_dxc_compile_flags_override.size() != 0) dxc_compile_flags_override = context.get_hooks().m_dxc_compile_flags_override; @@ -377,9 +404,8 @@ std::string CHLSLCompiler::preprocessShader(std::string&& code, IShader::E_SHADE if (context.get_hooks().m_pragmaStage != IShader::E_SHADER_STAGE::ESS_UNKNOWN) stage = context.get_hooks().m_pragmaStage; - if (dependencies) { - *dependencies = std::move(context.get_dependencies()); - } + if (dependenciesOut) + *dependenciesOut = std::move(context.get_dependencies()); } ); @@ -396,13 +422,29 @@ std::string CHLSLCompiler::preprocessShader(std::string&& code, IShader::E_SHADE } } + if (resolvedString.empty()) + return resolvedString; + + if (depfileEnabled) + { + IShaderCompiler::DepfileWriteParams params = {}; + const std::string depfilePathString = preprocessOptions.depfilePath.generic_string(); + params.depfilePath = depfilePathString; + params.sourceIdentifier = preprocessOptions.sourceIdentifier; + if (!params.sourceIdentifier.empty()) + params.workingDirectory = std::filesystem::path(std::string(params.sourceIdentifier)).parent_path(); + params.system = m_system.get(); + if (!IShaderCompiler::writeDepfile(params, *dependenciesOut, preprocessOptions.includeFinder, preprocessOptions.logger)) + return {}; + } + return resolvedString; } std::string CHLSLCompiler::preprocessShader(std::string&& code, IShader::E_SHADER_STAGE& stage, const SPreprocessorOptions& preprocessOptions, std::vector* dependencies) const { std::vector extra_dxc_compile_flags = {}; - return preprocessShader(std::move(code), stage, preprocessOptions, extra_dxc_compile_flags); + return preprocessShader(std::move(code), stage, preprocessOptions, extra_dxc_compile_flags, dependencies); } core::smart_refctd_ptr CHLSLCompiler::compileToSPIRV_impl(const std::string_view code, const IShaderCompiler::SCompilerOptions& options, std::vector* dependencies) const @@ -534,4 +576,4 @@ void CHLSLCompiler::insertIntoStart(std::string& code, std::ostringstream&& ins) code.insert(0u, ins.str()); } -#endif \ No newline at end of file +#endif diff --git a/src/nbl/asset/utils/CIESProfile.cpp b/src/nbl/asset/utils/CIESProfile.cpp index b507ab0d45..24f1427481 100644 --- a/src/nbl/asset/utils/CIESProfile.cpp +++ b/src/nbl/asset/utils/CIESProfile.cpp @@ -6,115 +6,30 @@ using namespace nbl; using namespace asset; -const CIESProfile::IES_STORAGE_FORMAT CIESProfile::sample(IES_STORAGE_FORMAT theta, IES_STORAGE_FORMAT phi) const +template +core::smart_refctd_ptr CIESProfile::createIESTexture(ExecutionPolicy&& policy, hlsl::uint32_t2 resolution) const { - auto wrapPhi = [&](const IES_STORAGE_FORMAT& _phi) -> IES_STORAGE_FORMAT - { - constexpr auto M_HALF_PI =core::HALF_PI(); - constexpr auto M_TWICE_PI = core::PI() * 2.0; - - switch (symmetry) - { - case ISOTROPIC: //! axial symmetry - return 0.0; - case QUAD_SYMETRIC: //! phi MIRROR_REPEAT wrap onto [0, 90] degrees range - { - float wrapPhi = abs(_phi); //! first MIRROR - - if (wrapPhi > M_HALF_PI) //! then REPEAT - wrapPhi = std::clamp(M_HALF_PI - (wrapPhi - M_HALF_PI), 0, M_HALF_PI); - - return wrapPhi; //! eg. maps (in degrees) 91,269,271 -> 89 and 179,181,359 -> 1 - } - case HALF_SYMETRIC: //! phi MIRROR wrap onto [0, 180] degrees range - case OTHER_HALF_SYMMETRIC: - return abs(_phi); //! eg. maps (in degress) 181 -> 179 or 359 -> 1 - case NO_LATERAL_SYMMET: //! plot onto whole (in degress) [0, 360] range - { - if (_phi < 0) - return _phi + M_TWICE_PI; - else - return _phi; - } - default: - assert(false); - return 69; - } - }; + uint32_t width = resolution.x; + uint32_t height = resolution.y; - const float vAngle = core::degrees(theta), hAngle = core::degrees(wrapPhi(phi)); + if (width > texture_t::MaxTextureWidth) + width = texture_t::MaxTextureWidth; - assert(vAngle >= 0.0 && vAngle <= 180.0); - assert(hAngle >= 0.0 && hAngle <= 360.0); + if (height > texture_t::MaxTextureHeight) + height = texture_t::MaxTextureHeight; - if (vAngle > vAngles.back()) - return 0.0; + width = core::max(width, texture_t::MinTextureWidth); + height = core::max(height, texture_t::MinTextureHeight); - // bilinear interpolation - auto lb = [](const core::vector& angles, double angle) -> size_t + auto makeOdd = [](uint32_t value, const uint32_t maxValue) -> uint32_t { - assert(!angles.empty()); - const size_t idx = std::upper_bound(std::begin(angles), std::end(angles), angle) - std::begin(angles); - return (size_t)std::max((int64_t)idx - 1, (int64_t)0); + if (value & 1u) + return value; + return (value < maxValue) ? (value + 1u) : (value - 1u); }; - - auto ub = [](const core::vector& angles, double angle) -> size_t - { - assert(!angles.empty()); - const size_t idx = std::upper_bound(std::begin(angles), std::end(angles), angle) - std::begin(angles); - return std::min(idx, angles.size() - 1); - }; - - const size_t j0 = lb(vAngles, vAngle); - const size_t j1 = ub(vAngles, vAngle); - const size_t i0 = symmetry == ISOTROPIC ? 0 : lb(hAngles, hAngle); - const size_t i1 = symmetry == ISOTROPIC ? 0 : ub(hAngles, hAngle); - - double uResp = i1 == i0 ? 1.0 : 1.0 / (hAngles[i1] - hAngles[i0]); - double vResp = j1 == j0 ? 1.0 : 1.0 / (vAngles[j1] - vAngles[j0]); - - double u = (hAngle - hAngles[i0]) * uResp; - double v = (vAngle - vAngles[j0]) * vResp; - - double s0 = getCandelaValue(i0, j0) * (1.0 - v) + getCandelaValue(i0, j1) * (v); - double s1 = getCandelaValue(i1, j0) * (1.0 - v) + getCandelaValue(i1, j1) * (v); - - return s0 * (1.0 - u) + s1 * u; -} - -inline core::vectorSIMDf CIESProfile::octahdronUVToDir(const float& u, const float& v) -{ - core::vectorSIMDf pos = core::vectorSIMDf(2 * (u - 0.5), 2 * (v - 0.5), 0.0); - float abs_x = core::abs(pos.x), abs_y = core::abs(pos.y); - pos.z = 1.0 - abs_x - abs_y; - if (pos.z < 0.0) { - pos.x = core::sign(pos.x) * (1.0 - abs_y); - pos.y = core::sign(pos.y) * (1.0 - abs_x); - } - - return core::normalize(pos); -} - - -inline std::pair CIESProfile::sphericalDirToRadians(const core::vectorSIMDf& dir) -{ - const float theta = std::acos(std::clamp(dir.z, -1.f, 1.f)); - const float phi = std::atan2(dir.y, dir.x); - - return { theta, phi }; -} - -template -core::smart_refctd_ptr CIESProfile::createIESTexture(ExecutionPolicy&& policy, const float flatten, const bool fullDomainFlatten, uint32_t width, uint32_t height) const -{ - const bool inFlattenDomain = flatten >= 0.0 && flatten <= 1.0; // [0, 1] range for blend equation, 1 is normally invalid but we use it to for special implied domain flatten mode - assert(inFlattenDomain); - - if (width > CDC_MAX_TEXTURE_WIDTH) - width = CDC_MAX_TEXTURE_WIDTH; - - if (height > CDC_MAX_TEXTURE_HEIGHT) - height = CDC_MAX_TEXTURE_HEIGHT; + // TODO: remove this once we exploit symmetries and fold the domain. + width = makeOdd(width, texture_t::MaxTextureWidth); + height = makeOdd(height, texture_t::MaxTextureHeight); asset::ICPUImage::SCreationParams imgInfo; imgInfo.type = asset::ICPUImage::ET_2D; @@ -125,21 +40,24 @@ core::smart_refctd_ptr CIESProfile::createIESTexture(Execu imgInfo.arrayLayers = 1u; imgInfo.samples = asset::ICPUImage::ESCF_1_BIT; imgInfo.flags = static_cast(0u); - imgInfo.format = IES_TEXTURE_STORAGE_FORMAT; + imgInfo.format = properties_t::IES_TEXTURE_STORAGE_FORMAT; auto outImg = asset::ICPUImage::create(std::move(imgInfo)); asset::ICPUImage::SBufferCopy region; - constexpr auto texelBytesz = asset::getTexelOrBlockBytesize(); + constexpr auto texelBytesz = asset::getTexelOrBlockBytesize(); const size_t bufferRowLength = asset::IImageAssetHandlerBase::calcPitchInBlocks(width, texelBytesz); region.bufferRowLength = bufferRowLength; region.imageExtent = imgInfo.extent; region.imageSubresource.baseArrayLayer = 0u; region.imageSubresource.layerCount = 1u; region.imageSubresource.mipLevel = 0u; + region.imageSubresource.aspectMask = core::bitflag(asset::IImage::EAF_COLOR_BIT); region.bufferImageHeight = 0u; region.bufferOffset = 0u; - auto buffer = core::make_smart_refctd_ptr(texelBytesz * bufferRowLength * height); + asset::ICPUBuffer::SCreationParams bParams; + bParams.size = texelBytesz * bufferRowLength * height; + auto buffer = asset::ICPUBuffer::create(std::move(bParams)); if (!outImg->setBufferAndRegions(std::move(buffer), core::make_refctd_dynamic_array>(1ull, region))) return {}; @@ -150,46 +68,28 @@ core::smart_refctd_ptr CIESProfile::createIESTexture(Execu CFillImageFilter::state_type state; state.outImage = outImg.get(); - state.subresource.aspectMask = static_cast(0); + state.subresource.aspectMask = core::bitflag(asset::IImage::EAF_COLOR_BIT); state.subresource.baseArrayLayer = 0u; state.subresource.layerCount = 1u; state.outRange.extent = creationParams.extent; const IImageFilter::IState::ColorValue::WriteMemoryInfo wInfo(creationParams.format, outImg->getBuffer()->getPointer()); + const auto texture = texture_t::create(accessor.properties.maxCandelaValue, hlsl::uint32_t2(width, height)); - // Late Optimization TODO: Modify the Max Value for the UNORM texture to be the Max Value after flatten blending - const double maxValue = getMaxCandelaValue(); - const double maxValueRecip = 1.0 / maxValue; - - const double vertInv = 1.0 / height; - const double horiInv = 1.0 / width; - - const double flattenTarget = getAvgEmmision(fullDomainFlatten); - const double domainLo = core::radians(vAngles.front()); - const double domainHi = core::radians(vAngles.back()); auto fill = [&](uint32_t blockArrayOffset, core::vectorSIMDu32 position) -> void { - const auto dir = octahdronUVToDir(((float)position.x + 0.5) * vertInv, ((float)position.y + 0.5) * horiInv); - const auto [theta, phi] = sphericalDirToRadians(dir); - const auto intensity = sample(theta, phi); - - //! blend the IES texture with "flatten" - double blendV = intensity * (1.0 - flatten); - if (fullDomainFlatten && domainLo<=theta && theta<=domainHi || intensity >0.0) - blendV += flattenTarget * flatten; - - blendV *= maxValueRecip; + const auto texel = texture.__call(accessor, hlsl::uint32_t2(position.x, position.y)); asset::IImageFilter::IState::ColorValue color; - //asset::encodePixels(color.asDouble, &blendV); TODO: FIX THIS ENCODE, GIVES ARTIFACTS - const uint16_t encodeV = static_cast(std::clamp(blendV * UI16_MAX_D + 0.5, 0.0, UI16_MAX_D)); + constexpr float UI16_MAX_D = static_cast(std::numeric_limits::max()); + const uint16_t encodeV = static_cast(std::clamp(texel * UI16_MAX_D + 0.5f, 0.f, UI16_MAX_D)); // TODO: use asset::encodePixels when its fixed (no artifacts) *color.asUShort = encodeV; color.writeMemory(wInfo, blockArrayOffset); }; CBasicImageFilterCommon::clip_region_functor_t clip(state.subresource, state.outRange, creationParams.format); const auto& regions = outImg->getRegions(state.subresource.mipLevel); - CBasicImageFilterCommon::executePerRegion(std::forward(policy), outImg.get(), fill, regions.begin(), regions.end(), clip); + CBasicImageFilterCommon::executePerRegion(std::forward(policy), outImg.get(), fill, regions, clip); } ICPUImageView::SCreationParams viewParams = {}; @@ -197,7 +97,7 @@ core::smart_refctd_ptr CIESProfile::createIESTexture(Execu viewParams.flags = static_cast(0); viewParams.viewType = IImageView::ET_2D; viewParams.format = viewParams.image->getCreationParameters().format; - viewParams.subresourceRange.aspectMask = static_cast(0); + viewParams.subresourceRange.aspectMask = core::bitflag(asset::IImage::EAF_COLOR_BIT); viewParams.subresourceRange.levelCount = viewParams.image->getCreationParameters().mipLevels; viewParams.subresourceRange.layerCount = 1u; @@ -206,11 +106,11 @@ core::smart_refctd_ptr CIESProfile::createIESTexture(Execu } //! Explicit instantiations -template core::smart_refctd_ptr CIESProfile::createIESTexture(const std::execution::sequenced_policy&, const float, const bool, uint32_t, uint32_t) const; -template core::smart_refctd_ptr CIESProfile::createIESTexture(const std::execution::parallel_policy&, const float, const bool, uint32_t, uint32_t) const; -template core::smart_refctd_ptr CIESProfile::createIESTexture(const std::execution::parallel_unsequenced_policy&, const float, const bool, uint32_t, uint32_t) const; +template core::smart_refctd_ptr CIESProfile::createIESTexture(const std::execution::sequenced_policy&, hlsl::uint32_t2) const; +template core::smart_refctd_ptr CIESProfile::createIESTexture(const std::execution::parallel_policy&, hlsl::uint32_t2) const; +template core::smart_refctd_ptr CIESProfile::createIESTexture(const std::execution::parallel_unsequenced_policy&, hlsl::uint32_t2) const; -core::smart_refctd_ptr CIESProfile::createIESTexture(const float flatten, const bool fullDomainFlatten, uint32_t width, uint32_t height) const +core::smart_refctd_ptr CIESProfile::createIESTexture(hlsl::uint32_t2 resolution) const { - return createIESTexture(std::execution::seq, flatten, fullDomainFlatten, width, height); -} \ No newline at end of file + return createIESTexture(std::execution::seq, resolution); +} diff --git a/src/nbl/asset/utils/CIESProfile.h b/src/nbl/asset/utils/CIESProfile.h index ea3d539613..f84f2753e5 100644 --- a/src/nbl/asset/utils/CIESProfile.h +++ b/src/nbl/asset/utils/CIESProfile.h @@ -6,135 +6,72 @@ #define __NBL_ASSET_C_IES_PROFILE_H_INCLUDED__ #include "nbl/asset/metadata/CIESProfileMetadata.h" -#include "nbl/core/Types.h" -#include +#include "nbl/builtin/hlsl/ies/profile.hlsl" +namespace nbl { namespace hlsl { namespace ies { struct SProceduralTexture; } } } namespace nbl { - namespace asset - { - class CIESProfile - { - public: - using IES_STORAGE_FORMAT = double; - - //! max 16K resolution - _NBL_STATIC_INLINE_CONSTEXPR size_t CDC_MAX_TEXTURE_WIDTH = 15360; - _NBL_STATIC_INLINE_CONSTEXPR size_t CDC_MAX_TEXTURE_HEIGHT = 8640; - - _NBL_STATIC_INLINE_CONSTEXPR size_t CDC_DEFAULT_TEXTURE_WIDTH = 1024; - _NBL_STATIC_INLINE_CONSTEXPR size_t CDC_DEFAULT_TEXTURE_HEIGHT = 1024; - - _NBL_STATIC_INLINE_CONSTEXPR IES_STORAGE_FORMAT MAX_VANGLE = 180.0; - _NBL_STATIC_INLINE_CONSTEXPR IES_STORAGE_FORMAT MAX_HANGLE = 360.0; - - _NBL_STATIC_INLINE_CONSTEXPR auto UI16_MAX_D = 65535.0; - _NBL_STATIC_INLINE_CONSTEXPR auto IES_TEXTURE_STORAGE_FORMAT = asset::EF_R16_UNORM; - - enum Version : uint8_t - { - V_1995, - V_2002, - V_SIZE - }; - - enum PhotometricType : uint8_t - { - TYPE_NONE, - TYPE_C, - TYPE_B, - TYPE_A - }; - - enum LuminairePlanesSymmetry : uint8_t - { - ISOTROPIC, //! Only one horizontal angle present and a luminaire is assumed to be laterally axial symmetric - QUAD_SYMETRIC, //! The luminaire is assumed to be symmetric in each quadrant - HALF_SYMETRIC, //! The luminaire is assumed to be symmetric about the 0 to 180 degree plane - OTHER_HALF_SYMMETRIC, //! HALF_SYMETRIC case for legacy V_1995 version where horizontal angles are in range [90, 270], in that case the parser patches horizontal angles to be HALF_SYMETRIC - NO_LATERAL_SYMMET //! The luminaire is assumed to exhibit no lateral symmet - }; - - CIESProfile() = default; - ~CIESProfile() = default; - - auto getType() const { return type; } - auto getVersion() const { return version; } - auto getSymmetry() const { return symmetry; } - - const core::vector& getHoriAngles() const { return hAngles; } - const core::vector& getVertAngles() const { return vAngles; } - const core::vector& getData() const { return data; } - IES_STORAGE_FORMAT getCandelaValue(size_t i, size_t j) const { return data[vAngles.size() * i + j]; } - - IES_STORAGE_FORMAT getMaxCandelaValue() const { return maxCandelaValue; } - IES_STORAGE_FORMAT getTotalEmission() const { return totalEmissionIntegral; } - inline IES_STORAGE_FORMAT getAvgEmmision(const bool fullDomain=false) const - { - if (fullDomain) - return totalEmissionIntegral*0.25/core::radians(vAngles.back()-vAngles.front()); - return avgEmmision; - } - - auto getOptimalIESResolution() const { return optimalIESResolution; } - - template - core::smart_refctd_ptr createIESTexture(ExecutionPolicy&& policy, const float flatten = 0.0, const bool fullDomainFlatten=false, uint32_t width = CDC_DEFAULT_TEXTURE_WIDTH, uint32_t height = CDC_DEFAULT_TEXTURE_HEIGHT) const; - core::smart_refctd_ptr createIESTexture(const float flatten = 0.0, const bool fullDomainFlatten=false, uint32_t width = CDC_DEFAULT_TEXTURE_WIDTH, uint32_t height = CDC_DEFAULT_TEXTURE_HEIGHT) const; - - private: - CIESProfile(PhotometricType type, size_t hSize, size_t vSize) - : type(type), version(V_SIZE), hAngles(hSize), vAngles(vSize), data(hSize* vSize) {} - - // TODO for @Hazard, I would move it into separate file, we may use this abstraction somewhere too - //! Returns spherical coordinates with physics convention in radians - /* - https://en.wikipedia.org/wiki/Spherical_coordinate_system#/media/File:3D_Spherical.svg - Retval.first is "theta" polar angle in range [0, PI] & Retval.second "phi" is azimuthal angle - in range [-PI, PI] range - - Cartesian coordinates obtained from the spherical coordinates in Nabla - are assumed to have radius equal to 1 and therefore always are - - x = cos(phi)*sin(theta) - y = sin(phi)*sin(theta) - z = cos(theta) - */ - - static inline std::pair sphericalDirToRadians(const core::vectorSIMDf& dir); - - //! Octahedral coordinate mapping is following - /* - center is Z- - U+ from center is X+ - V+ from center is Y+ - - when viewed as a texture, the net folds, and the apex where the seams join is Z+ - */ - - static inline core::vectorSIMDf octahdronUVToDir(const float& u, const float& v); - - void setCandelaValue(size_t i, size_t j, IES_STORAGE_FORMAT val) { data[vAngles.size() * i + j] = val; } - - const IES_STORAGE_FORMAT sample(IES_STORAGE_FORMAT vAngle, IES_STORAGE_FORMAT hAngle) const; - - PhotometricType type; - Version version; - LuminairePlanesSymmetry symmetry; - - core::vector hAngles; //! The angular displacement indegreesfrom straight down, a value represents spherical coordinate "theta" with physics convention. Note that if symmetry is OTHER_HALF_SYMMETRIC then real horizontal angle provided by IES data is (hAngles[index] + 90) - the reason behind it is we patch 1995 IES OTHER_HALF_SYMETRIC case to be HALF_SYMETRIC - core::vector vAngles; //! Measurements in degrees of angular displacement measured counterclockwise in a horizontal plane for Type C photometry and clockwise for Type A and B photometry, a value represents spherical coordinate "phi" with physics convention - core::vector data; //! Candela values - - IES_STORAGE_FORMAT maxCandelaValue = {}; //! Max value from this->data vector - IES_STORAGE_FORMAT totalEmissionIntegral = {}; //! Total energy emitted - IES_STORAGE_FORMAT avgEmmision = {}; //! this->totalEmissionIntegral / +namespace asset +{ +class CIESProfile +{ + public: + CIESProfile() = default; + ~CIESProfile() = default; - core::vector2du32_SIMD optimalIESResolution; //! optimal resolution for IES profile texture + struct properties_t : public nbl::hlsl::ies::ProfileProperties + { + using base_t = nbl::hlsl::ies::ProfileProperties; + NBL_CONSTEXPR_STATIC_INLINE auto IES_TEXTURE_STORAGE_FORMAT = asset::EF_R16_UNORM; + hlsl::uint32_t2 optimalIESResolution; //! Optimal resolution for IES Octahedral Candela Map texture + }; - friend class CIESProfileParser; + struct accessor_t + { + using angle_t = hlsl::float32_t; + using candela_t = hlsl::float32_t; + + accessor_t() = default; + accessor_t(const hlsl::uint32_t2& resolution, const properties_t& props) : hAngles(resolution.x), vAngles(resolution.y), data(resolution.x * resolution.y), properties(props) {} + ~accessor_t() = default; + + inline angle_t vAngle(const uint32_t idx) const { return vAngles[idx]; } + inline angle_t hAngle(const uint32_t idx) const { return hAngles[idx]; } + inline uint32_t vAnglesCount() const { return static_cast(vAngles.size()); } + inline uint32_t hAnglesCount() const { return static_cast(hAngles.size()); } + inline candela_t value(hlsl::uint32_t2 ij) const { const uint32_t vCount = static_cast(vAngles.size()); return data[vCount * ij.x + ij.y]; } + inline void setValue(hlsl::uint32_t2 ij, candela_t val) { const uint32_t vCount = static_cast(vAngles.size()); data[vCount * ij.x + ij.y] = val; } + + inline properties_t::base_t getProperties() const { return properties; } + + core::vector hAngles; //! The angular displacement indegreesfrom straight down, a value represents spherical coordinate "theta" with physics convention. Note that if symmetry is OTHER_HALF_SYMMETRIC then real horizontal angle provided by IES data is (hAngles[index] + 90) - the reason behind it is we patch 1995 IES OTHER_HALF_SYMETRIC case to be HALF_SYMETRIC + core::vector vAngles; //! Measurements in degrees of angular displacement measured counterclockwise in a horizontal plane for Type C photometry and clockwise for Type A and B photometry, a value represents spherical coordinate "phi" with physics convention + core::vector data; //! Candela scalar values + properties_t properties; //! Profile properties }; - } + using texture_t = nbl::hlsl::ies::SProceduralTexture; + + inline const accessor_t& getAccessor() const { return accessor; } + inline float getMaxCandelaValue() const { return accessor.properties.maxCandelaValue; } + inline hlsl::uint32_t2 getOptimalIESResolution() const { return accessor.properties.optimalIESResolution; } + inline float getAvgEmmision(const bool fullDomain) const { return fullDomain ? accessor.properties.fullDomainAvgEmission : accessor.properties.avgEmmision; } + + template + core::smart_refctd_ptr createIESTexture(ExecutionPolicy&& policy, hlsl::uint32_t2 resolution) const; + core::smart_refctd_ptr createIESTexture(hlsl::uint32_t2 resolution) const; + + template + inline core::smart_refctd_ptr createIESTexture(ExecutionPolicy&& policy) const { const auto res = getOptimalIESResolution(); return createIESTexture(policy, res); } + inline core::smart_refctd_ptr createIESTexture() const { const auto res = getOptimalIESResolution(); return createIESTexture(res); } + + private: + CIESProfile(const properties_t& props, const hlsl::uint32_t2& resolution) : accessor(resolution, props) {} + accessor_t accessor; + friend class CIESProfileParser; +}; +} } -#endif // __NBL_ASSET_C_IES_PROFILE_H_INCLUDED__ \ No newline at end of file +#include "nbl/builtin/hlsl/ies/texture.hlsl" + +#endif // __NBL_ASSET_C_IES_PROFILE_H_INCLUDED__ diff --git a/src/nbl/asset/utils/CIESProfileParser.cpp b/src/nbl/asset/utils/CIESProfileParser.cpp index e0593cd7f2..66196b1902 100644 --- a/src/nbl/asset/utils/CIESProfileParser.cpp +++ b/src/nbl/asset/utils/CIESProfileParser.cpp @@ -17,7 +17,7 @@ bool CIESProfileParser::parse(CIESProfile& result) { auto removeTrailingWhiteChars = [](std::string& str) -> void { - if (std::isspace(str.back())) + if (!str.empty() && std::isspace(static_cast(str.back()))) { auto it = str.rbegin(); while (it != str.rend() && std::isspace(static_cast(*it))) @@ -33,16 +33,16 @@ bool CIESProfileParser::parse(CIESProfile& result) std::getline(ss, line); removeTrailingWhiteChars(line); - CIESProfile::Version iesVersion; - + + CIESProfile::properties_t::Version iesVersion; if (line.find(SIG_LM63_1995.data()) != std::string::npos) - iesVersion = CIESProfile::V_1995; + iesVersion = CIESProfile::properties_t::V_1995; else if (line.find(SIG_LM63_2002.data()) != std::string::npos) - iesVersion = CIESProfile::V_2002; + iesVersion = CIESProfile::properties_t::V_2002; else if (line.find(SIG_IESNA91.data()) != std::string::npos) - iesVersion = CIESProfile::V_1995; + iesVersion = CIESProfile::properties_t::V_1995; else if (line.find(SIG_ERCO_LG.data()) != std::string::npos) - iesVersion = CIESProfile::V_1995; + iesVersion = CIESProfile::properties_t::V_1995; else { errorMsg = "Unknown IESNA:LM-63 version, the IES input being parsed is invalid!"; @@ -82,9 +82,8 @@ bool CIESProfileParser::parse(CIESProfile& result) errorMsg = "unrecognized type"; return false; } - CIESProfile::PhotometricType type = - static_cast(type_); - if (type != CIESProfile::PhotometricType::TYPE_C) { + auto type = static_cast(type_); + if (type != CIESProfile::properties_t::TYPE_C) { errorMsg = "Only type C is supported for now"; return false; } @@ -100,38 +99,42 @@ bool CIESProfileParser::parse(CIESProfile& result) if (error) return false; - result = CIESProfile(type, hSize, vSize); - result.version = iesVersion; + { + CIESProfile::properties_t init; + init.setType(type); + init.setVersion(iesVersion); + init.maxCandelaValue = 0.f; + init.totalEmissionIntegral = 0.f; + init.avgEmmision = 0.f; + result = CIESProfile(init, hlsl::uint32_t2(hSize, vSize)); + } if (vSize < 2) return false; - { - const uint32_t maxDimMeasureSize = core::max(hSize, vSize); - result.optimalIESResolution = decltype(result.optimalIESResolution){ maxDimMeasureSize, maxDimMeasureSize }; - result.optimalIESResolution *= 2u; // safe bias for our bilinear interpolation to work nicely and increase resolution of a profile - } + using angle_t = CIESProfile::accessor_t::angle_t; + using candela_t = CIESProfile::accessor_t::candela_t; - auto& vAngles = result.vAngles; + auto& vAngles = result.accessor.vAngles; for (int i = 0; i < vSize; i++) { - vAngles[i] = getDouble("vertical angle truncated"); + vAngles[i] = static_cast(getDouble("vertical angle truncated")); } if (!std::is_sorted(vAngles.begin(), vAngles.end())) { errorMsg = "Vertical angles should be sorted"; return false; } - if (vAngles[0] != 0.0 && vAngles[0] != 90.0) { + if (vAngles[0] != 0.f && vAngles[0] != 90.f) { errorMsg = "First vertical angle must be 0 or 90 in type C"; return false; } - if (vAngles[vSize - 1] != 90.0 && vAngles[vSize - 1] != 180.0) { + if (vAngles[vSize - 1] != 90.f && vAngles[vSize - 1] != 180.f) { errorMsg = "Last vertical angle must be 90 or 180 in type C"; return false; } - auto& hAngles = result.hAngles; + auto& hAngles = result.accessor.hAngles; for (int i = 0; i < hSize; i++) { - hAngles[i] = getDouble("horizontal angle truncated"); + hAngles[i] = static_cast(getDouble("horizontal angle truncated")); if (i != 0 && hAngles[i - 1] > hAngles[i]) return false; // Angles should be sorted } @@ -141,26 +144,26 @@ bool CIESProfileParser::parse(CIESProfile& result) const auto firstHAngle = hAngles.front(); const auto lastHAngle = hAngles.back(); - if (lastHAngle == 0) - result.symmetry = CIESProfile::ISOTROPIC; - else if (lastHAngle == 90) + if (lastHAngle == 0.f) + result.accessor.properties.setSymmetry(CIESProfile::properties_t::ISOTROPIC); + else if (lastHAngle == 90.f) { - result.symmetry = CIESProfile::QUAD_SYMETRIC; - fluxMultiplier = 4.0; + result.accessor.properties.setSymmetry(CIESProfile::properties_t::QUAD_SYMETRIC); + fluxMultiplier = 4.f; } - else if (lastHAngle == 180) + else if (lastHAngle == 180.f) { - result.symmetry = CIESProfile::HALF_SYMETRIC; + result.accessor.properties.setSymmetry(CIESProfile::properties_t::HALF_SYMETRIC); fluxMultiplier = 2.0; } - else if (lastHAngle == 360) - result.symmetry = CIESProfile::NO_LATERAL_SYMMET; + else if (lastHAngle == 360.f) + result.accessor.properties.setSymmetry(CIESProfile::properties_t::NO_LATERAL_SYMMET); else { - if (firstHAngle == 90 && lastHAngle == 270 && result.version == CIESProfile::V_1995) + if (firstHAngle == 90.f && lastHAngle == 270.f && iesVersion == CIESProfile::properties_t::V_1995) { - result.symmetry = CIESProfile::OTHER_HALF_SYMMETRIC; - fluxMultiplier = 2.0; + result.accessor.properties.setSymmetry(CIESProfile::properties_t::OTHER_HALF_SYMMETRIC); + fluxMultiplier = 2.f; for (auto& angle : hAngles) angle -= firstHAngle; // patch the profile to HALF_SYMETRIC by shifting [90,270] range to [0, 180] @@ -169,57 +172,81 @@ bool CIESProfileParser::parse(CIESProfile& result) return false; } } + const auto symmetry = result.accessor.properties.getSymmetry(); { const double factor = ballastFactor * candelaMultiplier; for (int i = 0; i < hSize; i++) for (int j = 0; j < vSize; j++) - result.setCandelaValue(i, j, factor * getDouble("intensity value truncated")); + result.accessor.setValue(hlsl::uint32_t2(i, j), static_cast(factor * getDouble("intensity value truncated"))); } float totalEmissionIntegral = 0.0, nonZeroEmissionDomainSize = 0.0; constexpr auto FULL_SOLID_ANGLE = 4.0f * core::PI(); - const auto H_ANGLES_I_RANGE = result.symmetry != CIESProfile::ISOTROPIC ? result.hAngles.size() - 1 : 1; - const auto V_ANGLES_I_RANGE = result.vAngles.size() - 1; + // TODO: this code could have two separate inner for loops for `result.symmetry != CIESProfile::ISOTROPIC` cases + const auto H_ANGLES_I_RANGE = symmetry != CIESProfile::properties_t::ISOTROPIC ? result.accessor.hAngles.size() - 1 : 1; + const auto V_ANGLES_I_RANGE = result.accessor.vAngles.size() - 1; - for (size_t i = 0; i < H_ANGLES_I_RANGE; i++) + float smallestRangeSolidAngle = FULL_SOLID_ANGLE; + for (size_t j = 0; j < V_ANGLES_I_RANGE; j++) { - const float dPhiRad = result.symmetry != CIESProfile::ISOTROPIC ? (hAngles[i + 1] - hAngles[i]) : core::PI() * 2.0f; - - for (size_t j = 0; j < V_ANGLES_I_RANGE; j++) + const float thetaRad = core::radians(result.accessor.vAngles[j]); + const float cosLo = std::cos(thetaRad); + const float cosHi = std::cos(core::radians(result.accessor.vAngles[j+1])); + const float dsinTheta = cosLo - cosHi; + + float stripIntegral = 0.f; + float nonZeroStripDomain = 0.f; + for (size_t i = 0; i < H_ANGLES_I_RANGE; i++) { - const auto candelaValue = result.getCandelaValue(i, j); - - // interpolate candela value spanned onto a solid angle - const auto candelaAverage = result.symmetry != CIESProfile::ISOTROPIC ? - 0.25f * (candelaValue + result.getCandelaValue(i + 1, j) + result.getCandelaValue(i, j + 1) + result.getCandelaValue(i + 1, j + 1)) - : 0.5f * (candelaValue + result.getCandelaValue(i, j + 1)); + const float dPhiRad = symmetry != CIESProfile::properties_t::ISOTROPIC ? core::radians(hAngles[i + 1] - hAngles[i]) : (core::PI() * 2.0f); + // TODO: in reality one should transform the 4 vertices (or 3) into octahedral map, work out the dUV/dPhi and dUV/dTheta vectors as-if for Anisotropic Filtering + // then choose the minor axis length, and use that as a pixel size (since looking for smallest thing, dont have to worry about handling discont) + const float solidAngle = dsinTheta * dPhiRad; + if (solidAngle(result.vAngles[j]); - const float cosLo = std::cos(core::radians(result.vAngles[j])); - const float cosHi = std::cos(core::radians(result.vAngles[j + 1])); + // interpolate candela value spanned onto a solid angle + const auto candelaAverage = symmetry != CIESProfile::properties_t::ISOTROPIC ? + 0.25f * (candelaValue + result.accessor.value(hlsl::uint32_t2(i + 1, j)) + result.accessor.value(hlsl::uint32_t2(i, j + 1)) + result.accessor.value(hlsl::uint32_t2(i + 1, j + 1))) + : 0.5f * (candelaValue + result.accessor.value(hlsl::uint32_t2(i, j + 1))); - const auto differentialSolidAngle = dPhiRad*(cosLo - cosHi); - const auto integralV = candelaAverage * differentialSolidAngle; + if (result.accessor.properties.maxCandelaValue < candelaValue) + result.accessor.properties.maxCandelaValue = candelaValue; - if (integralV > 0.0) - { - totalEmissionIntegral += integralV; - nonZeroEmissionDomainSize += differentialSolidAngle; - } + stripIntegral += candelaAverage*dPhiRad; + if (candelaAverage>0.f) + nonZeroStripDomain += dPhiRad; } + totalEmissionIntegral += stripIntegral*dsinTheta; + nonZeroEmissionDomainSize += nonZeroStripDomain*dsinTheta; } - nonZeroEmissionDomainSize = std::clamp(nonZeroEmissionDomainSize, 0.0, FULL_SOLID_ANGLE); - if (nonZeroEmissionDomainSize <= 0) // protect us from division by 0 (just in case, we should never hit it) + // assuming octahedral map + { + const uint32_t maxDimMeasureSize = core::sqrt(FULL_SOLID_ANGLE/smallestRangeSolidAngle); + result.accessor.properties.optimalIESResolution = decltype(result.accessor.properties.optimalIESResolution){ maxDimMeasureSize, maxDimMeasureSize }; + auto& res = result.accessor.properties.optimalIESResolution *= 2u; // safe bias for our bilinear interpolation to work nicely and increase resolution of a profile + res.x = core::max(res.x, CIESProfile::texture_t::MinTextureWidth); + res.y = core::max(res.y, CIESProfile::texture_t::MinTextureHeight); + } + + assert(nonZeroEmissionDomainSize >= 0.f); + //assert(nonZeroEmissionDomainSize*fluxMultiplier =approx= 2.f*(cosBack-cosFront)*PI); + if (nonZeroEmissionDomainSize <= std::numeric_limits::min()) // protect us from division by small numbers (just in case, we should never hit it) return false; - result.avgEmmision = totalEmissionIntegral / static_cast(nonZeroEmissionDomainSize); - result.totalEmissionIntegral = totalEmissionIntegral * fluxMultiplier; // we use fluxMultiplier to calculate final total emission for case where we have some symmetry between planes (fluxMultiplier is 1.0f if ISOTROPIC or NO_LATERAL_SYMMET because they already have correct total emission integral calculated), also note it doesn't affect average emission at all + result.accessor.properties.avgEmmision = totalEmissionIntegral / static_cast(nonZeroEmissionDomainSize); + result.accessor.properties.totalEmissionIntegral = totalEmissionIntegral * fluxMultiplier; // we use fluxMultiplier to calculate final total emission for case where we have some symmetry between planes (fluxMultiplier is 1.0f if ISOTROPIC or NO_LATERAL_SYMMET because they already have correct total emission integral calculated), also note it doesn't affect average emission at all + { + const float cosLo = std::cos(core::radians(result.accessor.vAngles.front())); + const float cosHi = std::cos(core::radians(result.accessor.vAngles.back())); + const float dsinTheta = cosLo - cosHi; + result.accessor.properties.fullDomainAvgEmission = result.accessor.properties.totalEmissionIntegral*(0.5f/core::PI())/dsinTheta; + } return !error; -} \ No newline at end of file +} diff --git a/src/nbl/asset/utils/CIESProfileParser.h b/src/nbl/asset/utils/CIESProfileParser.h index cc613efc29..c5b57dd77b 100644 --- a/src/nbl/asset/utils/CIESProfileParser.h +++ b/src/nbl/asset/utils/CIESProfileParser.h @@ -99,7 +99,7 @@ namespace nbl if (!this->errorMsg) this->errorMsg = errorMsg; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) return -1.0; else return 0; diff --git a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp index 3227ea9958..b4f2f2ef06 100644 --- a/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp +++ b/src/nbl/asset/utils/CPolygonGeometryManipulator.cpp @@ -11,11 +11,15 @@ #include "nbl/asset/utils/CPolygonGeometryManipulator.h" #include "nbl/asset/utils/CVertexWelder.h" #include "nbl/asset/utils/CSmoothNormalGenerator.h" +#include "nbl/asset/utils/CForsythVertexCacheOptimizer.h" +#include "nbl/asset/utils/COverdrawPolygonGeometryOptimizer.h" +#include "nbl/asset/utils/COBBGenerator.h" namespace nbl::asset { + core::smart_refctd_ptr CPolygonGeometryManipulator::createUnweldedList(const ICPUPolygonGeometry* inGeo) { const auto* indexing = inGeo->getIndexingCallback(); @@ -161,33 +165,139 @@ core::smart_refctd_ptr CPolygonGeometryManipulator::createS } #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. +void IMeshManipulator::flipSurfaces(ICPUMeshBuffer* inbuffer) +{ + if (!inbuffer) + return; + auto* pipeline = inbuffer->getPipeline(); + const E_PRIMITIVE_TOPOLOGY primType = pipeline->getCachedCreationParams().primitiveAssembly.primitiveType; + + const uint32_t idxcnt = inbuffer->getIndexCount(); + if (!inbuffer->getIndices()) + return; + + if (inbuffer->getIndexType() == EIT_16BIT) + { + uint16_t* idx = reinterpret_cast(inbuffer->getIndices()); + switch (primType) + { + case EPT_TRIANGLE_FAN: + for (uint32_t i = 1; i < idxcnt; i += 2) + { + const uint16_t tmp = idx[i]; + idx[i] = idx[i + 1]; + idx[i + 1] = tmp; + } + break; + case EPT_TRIANGLE_STRIP: + if (idxcnt % 2) //odd + { + for (uint32_t i = 0; i < (idxcnt >> 1); i++) + { + const uint16_t tmp = idx[i]; + idx[i] = idx[idxcnt - 1 - i]; + idx[idxcnt - 1 - i] = tmp; + } + } + else //even + { + auto newIndexBuffer = ICPUBuffer::create({ (idxcnt + 1u) * sizeof(uint16_t) }); + auto* destPtr = reinterpret_cast(newIndexBuffer->getPointer()); + destPtr[0] = idx[0]; + memcpy(destPtr + 1u, idx, sizeof(uint16_t) * idxcnt); + inbuffer->setIndexCount(idxcnt + 1u); + SBufferBinding ixBufBinding{ 0u, std::move(newIndexBuffer) }; + inbuffer->setIndexBufferBinding(std::move(ixBufBinding)); + } + break; + case EPT_TRIANGLE_LIST: + for (uint32_t i = 0; i < idxcnt; i += 3) + { + const uint16_t tmp = idx[i + 1]; + idx[i + 1] = idx[i + 2]; + idx[i + 2] = tmp; + } + break; + default: break; + } + } + else if (inbuffer->getIndexType() == EIT_32BIT) + { + uint32_t* idx = reinterpret_cast(inbuffer->getIndices()); + switch (primType) + { + case EPT_TRIANGLE_FAN: + for (uint32_t i = 1; i < idxcnt; i += 2) + { + const uint32_t tmp = idx[i]; + idx[i] = idx[i + 1]; + idx[i + 1] = tmp; + } + break; + case EPT_TRIANGLE_STRIP: + if (idxcnt % 2) //odd + { + for (uint32_t i = 0; i < (idxcnt >> 1); i++) + { + const uint32_t tmp = idx[i]; + idx[i] = idx[idxcnt - 1 - i]; + idx[idxcnt - 1 - i] = tmp; + } + } + else //even + { + auto newIndexBuffer = ICPUBuffer::create({ (idxcnt + 1u) * sizeof(uint32_t) }); + auto* destPtr = reinterpret_cast(newIndexBuffer->getPointer()); + destPtr[0] = idx[0]; + memcpy(destPtr + 1u, idx, sizeof(uint32_t) * idxcnt); + inbuffer->setIndexCount(idxcnt + 1); + SBufferBinding ixBufBinding{ 0u, std::move(newIndexBuffer) }; + inbuffer->setIndexBufferBinding(std::move(ixBufBinding)); + } + break; + case EPT_TRIANGLE_LIST: + for (uint32_t i = 0; i < idxcnt; i += 3) + { + const uint32_t tmp = idx[i + 1]; + idx[i + 1] = idx[i + 2]; + idx[i + 2] = tmp; + } + break; + default: break; + } + } +} + core::smart_refctd_ptr CMeshManipulator::createMeshBufferFetchOptimized(const ICPUMeshBuffer* _inbuffer) { if (!_inbuffer) return nullptr; - const auto* pipeline = _inbuffer->getPipeline(); - const void* ind = _inbuffer->getIndices(); + const auto* pipeline = _inbuffer->getPipeline(); + const void* ind = _inbuffer->getIndices(); if (!pipeline || !ind) return nullptr; auto outbuffer = core::move_and_static_cast(_inbuffer->clone(1u)); - outbuffer->setAttachedDescriptorSet(core::smart_refctd_ptr(const_cast(_inbuffer->getAttachedDescriptorSet()))); - outbuffer->setSkin( - SBufferBinding(reinterpret_cast&>(_inbuffer->getInverseBindPoseBufferBinding())), - SBufferBinding(reinterpret_cast&>(_inbuffer->getJointAABBBufferBinding())), - _inbuffer->getJointCount(),_inbuffer->getMaxJointsPerVertex() - ); + outbuffer->setAttachedDescriptorSet(core::smart_refctd_ptr(const_cast(_inbuffer->getAttachedDescriptorSet()))); + outbuffer->setSkin( + SBufferBinding(reinterpret_cast&>(_inbuffer->getInverseBindPoseBufferBinding())), + SBufferBinding(reinterpret_cast&>(_inbuffer->getJointAABBBufferBinding())), + _inbuffer->getJointCount(),_inbuffer->getMaxJointsPerVertex() + ); - constexpr uint32_t MAX_ATTRIBS = asset::ICPUMeshBuffer::MAX_VERTEX_ATTRIB_COUNT; + constexpr uint32_t MAX_ATTRIBS = asset::ICPUMeshBuffer::MAX_VERTEX_ATTRIB_COUNT; // Find vertex count size_t vertexCount = IMeshManipulator::upperBoundVertexID(_inbuffer); core::unordered_set buffers; for (size_t i = 0; i < MAX_ATTRIBS; ++i) - if (auto* buf = _inbuffer->getAttribBoundBuffer(i).buffer.get()) - buffers.insert(buf); + if (auto* buf = _inbuffer->getAttribBoundBuffer(i).buffer.get()) + buffers.insert(buf); size_t offsets[MAX_ATTRIBS]; memset(offsets, -1, sizeof(offsets)); @@ -202,36 +312,36 @@ core::smart_refctd_ptr CMeshManipulator::createMeshBufferFetchOp { types[i] = _inbuffer->getAttribFormat(i); - const uint32_t typeSz = getTexelOrBlockBytesize(types[i]); - const size_t alignment = (typeSz/getFormatChannelCount(types[i]) == 8u) ? 8ull : 4ull; // if format 64bit per channel, then align to 8 + const uint32_t typeSz = getTexelOrBlockBytesize(types[i]); + const size_t alignment = (typeSz/getFormatChannelCount(types[i]) == 8u) ? 8ull : 4ull; // if format 64bit per channel, then align to 8 offsets[i] = lastOffset + lastSize; const size_t mod = offsets[i] % alignment; offsets[i] += mod; lastOffset = offsets[i]; - lastSize = typeSz; + lastSize = typeSz; } } const size_t vertexSize = lastOffset + lastSize; - constexpr uint32_t NEW_VTX_BUF_BINDING = 0u; - auto& vtxParams = outbuffer->getPipeline()->getCachedCreationParams().vertexInput; - vtxParams = SVertexInputParams(); - vtxParams.enabledAttribFlags = _inbuffer->getPipeline()->getCachedCreationParams().vertexInput.enabledAttribFlags; - vtxParams.enabledBindingFlags = 1u << NEW_VTX_BUF_BINDING; - vtxParams.bindings[NEW_VTX_BUF_BINDING].stride = vertexSize; - vtxParams.bindings[NEW_VTX_BUF_BINDING].inputRate = SVertexInputBindingParams::EVIR_PER_VERTEX; + constexpr uint32_t NEW_VTX_BUF_BINDING = 0u; + auto& vtxParams = outbuffer->getPipeline()->getCachedCreationParams().vertexInput; + vtxParams = SVertexInputParams(); + vtxParams.enabledAttribFlags = _inbuffer->getPipeline()->getCachedCreationParams().vertexInput.enabledAttribFlags; + vtxParams.enabledBindingFlags = 1u << NEW_VTX_BUF_BINDING; + vtxParams.bindings[NEW_VTX_BUF_BINDING].stride = vertexSize; + vtxParams.bindings[NEW_VTX_BUF_BINDING].inputRate = SVertexInputBindingParams::EVIR_PER_VERTEX; auto newVertBuffer = ICPUBuffer::create({ vertexCount*vertexSize }); - outbuffer->setVertexBufferBinding({ 0u, core::smart_refctd_ptr(newVertBuffer) }, NEW_VTX_BUF_BINDING); + outbuffer->setVertexBufferBinding({ 0u, core::smart_refctd_ptr(newVertBuffer) }, NEW_VTX_BUF_BINDING); for (size_t i = 0; i < MAX_ATTRIBS; ++i) { if (offsets[i] < 0xffffffff) { - vtxParams.attributes[i].binding = NEW_VTX_BUF_BINDING; - vtxParams.attributes[i].format = types[i]; - vtxParams.attributes[i].relativeOffset = offsets[i]; + vtxParams.attributes[i].binding = NEW_VTX_BUF_BINDING; + vtxParams.attributes[i].format = types[i]; + vtxParams.attributes[i].relativeOffset = offsets[i]; } } } @@ -261,7 +371,7 @@ core::smart_refctd_ptr CMeshManipulator::createMeshBufferFetchOp { E_FORMAT type = types[activeAttribs[j]]; - if (!isNormalizedFormat(type) && (isIntegerFormat(type) || isScaledFormat(type))) + if (!isNormalizedFormat(type) && (isIntegerFormat(type) || isScaledFormat(type))) { uint32_t dst[4]; _inbuffer->getAttribute(dst, activeAttribs[j], index); @@ -284,12 +394,1042 @@ core::smart_refctd_ptr CMeshManipulator::createMeshBufferFetchOp ((uint16_t*)indices)[i] = remap; } - _NBL_DELETE_ARRAY(remapBuffer,vertexCount); + _NBL_DELETE_ARRAY(remapBuffer,vertexCount); _NBL_DEBUG_BREAK_IF(nextVert > vertexCount) return outbuffer; } + +//! Creates a copy of the mesh, which will only consist of unique primitives +core::smart_refctd_ptr IMeshManipulator::createMeshBufferUniquePrimitives(ICPUMeshBuffer* inbuffer, bool _makeIndexBuf) +{ + if (!inbuffer) + return nullptr; + const ICPURenderpassIndependentPipeline* oldPipeline = inbuffer->getPipeline(); + if (!oldPipeline) + return nullptr; + + const uint32_t idxCnt = inbuffer->getIndexCount(); + if (idxCnt<2u || !inbuffer->getIndices()) + return core::smart_refctd_ptr(inbuffer); // yes we want an extra grab + + const auto& oldVtxParams = oldPipeline->getCachedCreationParams().vertexInput; + + auto clone = core::move_and_static_cast(inbuffer->clone(0u)); + + constexpr uint32_t NEW_VTX_BUF_BINDING = 0u; + + auto pipeline = core::smart_refctd_ptr_static_cast(oldPipeline->clone(0u)); + auto& vtxParams = pipeline->getCachedCreationParams().vertexInput; + vtxParams = SVertexInputParams(); + + vtxParams.enabledBindingFlags = (1u<getAttribBoundBuffer(i); + if (inbuffer->isAttributeEnabled(i) && vbuf.buffer) + { + offset[i] = stride; + newAttribSizes[i] = getTexelOrBlockBytesize(inbuffer->getAttribFormat(i)); + stride += newAttribSizes[i]; + if (stride>=0xdeadbeefu) + return nullptr; + + sourceBuffers[i] = reinterpret_cast(vbuf.buffer->getPointer()); + sourceBuffers[i] += inbuffer->getAttribCombinedOffset(i); + sourceBufferStrides[i] = inbuffer->getAttribStride(i); + } + else + offset[i] = -1; + } + + vtxParams.bindings[NEW_VTX_BUF_BINDING].inputRate = SVertexInputBindingParams::EVIR_PER_VERTEX; + vtxParams.bindings[NEW_VTX_BUF_BINDING].stride = stride; + + auto vertexBuffer = ICPUBuffer::create({ stride*idxCnt }); + clone->setVertexBufferBinding({0u, vertexBuffer}, 0u); + for (size_t i=0; i= 0) + { + vtxParams.attributes[i].binding = NEW_VTX_BUF_BINDING; + vtxParams.attributes[i].format = inbuffer->getAttribFormat(i); + vtxParams.attributes[i].relativeOffset = offset[i]; + } + } + + uint8_t* destPointer = reinterpret_cast(vertexBuffer->getPointer()); + if (inbuffer->getIndexType()==EIT_16BIT) + { + uint16_t* idx = reinterpret_cast(inbuffer->getIndices()); + for (uint64_t i=0; igetBaseVertex())*sourceBufferStrides[j],newAttribSizes[j]); + destPointer += newAttribSizes[j]; + } + } + else if (inbuffer->getIndexType()==EIT_32BIT) + { + uint32_t* idx = reinterpret_cast(inbuffer->getIndices()); + for (uint64_t i=0; igetBaseVertex())*sourceBufferStrides[j],newAttribSizes[j]); + destPointer += newAttribSizes[j]; + } + } + + clone->setPipeline(std::move(pipeline)); + + if (_makeIndexBuf) + { + auto idxbuf = ICPUBuffer::create({ idxCnt*(idxCnt<0x10000 ? 2u : 4u) }); + if (idxCnt<0x10000u) + { + for (uint32_t i = 0u; i < idxCnt; ++i) + reinterpret_cast(idxbuf->getPointer())[i] = i; + clone->setIndexType(EIT_16BIT); + } + else + { + for (uint32_t i = 0u; i < idxCnt; ++i) + reinterpret_cast(idxbuf->getPointer())[i] = i; + clone->setIndexType(EIT_32BIT); + } + clone->setIndexBufferBinding({ 0u, std::move(idxbuf) }); + } + else + { + clone->setIndexType(EIT_UNKNOWN); + } + } + + return clone; +} + +core::smart_refctd_ptr IMeshManipulator::createOptimizedMeshBuffer(const ICPUMeshBuffer* _inbuffer, const SErrorMetric* _errMetric) +{ + if (!_inbuffer) + return nullptr; + const auto oldPipeline = _inbuffer->getPipeline(); + auto outbuffer = core::move_and_static_cast(_inbuffer->clone(oldPipeline ? 1u:0u)); + if (!oldPipeline) + return outbuffer; + + // restore shared skeleton and descriptor set + outbuffer->setAttachedDescriptorSet(core::smart_refctd_ptr(const_cast(_inbuffer->getAttachedDescriptorSet()))); + outbuffer->setSkin( + SBufferBinding(reinterpret_cast&>(_inbuffer->getInverseBindPoseBufferBinding())), + SBufferBinding(reinterpret_cast&>(_inbuffer->getJointAABBBufferBinding())), + _inbuffer->getJointCount(),_inbuffer->getMaxJointsPerVertex() + ); + + // make index buffer 0,1,2,3,4,... if nothing's mapped + // make 32bit index buffer if 16bit one is present + // convert index buffer for triangle primitives + constexpr auto canonicalMeshBufferIndexType = EIT_32BIT; + IMeshManipulator::homogenizePrimitiveTypeAndIndices(&outbuffer.get(),&outbuffer.get()+1,EPT_TRIANGLE_LIST,canonicalMeshBufferIndexType); + if (outbuffer->getPipeline()->getCachedCreationParams().primitiveAssembly.primitiveType != EPT_TRIANGLE_LIST) + return nullptr; + + // STEP: weld + createMeshBufferWelded(outbuffer.get(), _errMetric, false, false); + + // STEP: filter invalid triangles + if (!_inbuffer->isSkinned()) + filterInvalidTriangles(outbuffer.get()); + + // STEP: overdraw optimization + COverdrawMeshOptimizer::createOptimized(outbuffer.get(),outbuffer.get()); + + // STEP: Forsyth + { + uint32_t* indices = reinterpret_cast(outbuffer->getIndices()); + CForsythVertexCacheOptimizer forsyth; + const uint32_t vertexCount = IMeshManipulator::upperBoundVertexID(_inbuffer); + forsyth.optimizeTriangleOrdering(vertexCount, outbuffer->getIndexCount(), indices, indices); + } + + // STEP: prefetch optimization + outbuffer = CMeshManipulator::createMeshBufferFetchOptimized(outbuffer.get()); // here we also get interleaved attributes (single vertex buffer) + + // STEP: requantization + requantizeMeshBuffer(outbuffer.get(), _errMetric); + + // STEP: reduce index buffer to 16bit or completely get rid of it + { + const void* const indices = outbuffer->getIndices(); + uint32_t* indicesCopy = (uint32_t*)_NBL_ALIGNED_MALLOC(outbuffer->getIndexCount()*4,_NBL_SIMD_ALIGNMENT); + memcpy(indicesCopy, indices, outbuffer->getIndexCount()*4); + std::sort(indicesCopy, indicesCopy + outbuffer->getIndexCount()); + + bool continuous = true; // indices are i.e. 0,1,2,3,4,5,... (also implies indices being unique) + bool unique = true; // indices are unique (but not necessarily continuos) + + for (size_t i = 0; i < outbuffer->getIndexCount(); ++i) + { + uint32_t idx = indicesCopy[i], prevIdx = 0xffffffffu; + if (i) + { + prevIdx = indicesCopy[i-1]; + + if (idx == prevIdx) + { + unique = false; + continuous = false; + break; + } + if (idx != prevIdx + 1) + continuous = false; + } + } + + const uint32_t minIdx = indicesCopy[0]; + const uint32_t maxIdx = indicesCopy[outbuffer->getIndexCount() - 1]; + + _NBL_ALIGNED_FREE(indicesCopy); + + core::smart_refctd_ptr newIdxBuffer; + bool verticesMustBeReordered = false; + E_INDEX_TYPE newIdxType = EIT_UNKNOWN; + + if (!continuous) + { + if (unique) + { + // no index buffer + // vertices have to be reordered + verticesMustBeReordered = true; + } + else + { + if (maxIdx - minIdx <= USHRT_MAX) + newIdxType = EIT_16BIT; + else + newIdxType = EIT_32BIT; + + outbuffer->setBaseVertex(outbuffer->getBaseVertex() + minIdx); + + if (newIdxType == EIT_16BIT) + { + newIdxBuffer = ICPUBuffer::create({ sizeof(uint16_t)*outbuffer->getIndexCount() }); + // no need to change index buffer offset because it's always 0 (after duplicating original mesh) + for (size_t i = 0; i < outbuffer->getIndexCount(); ++i) + reinterpret_cast(newIdxBuffer->getPointer())[i] = reinterpret_cast(indices)[i] - minIdx; + } + } + } + else + { + outbuffer->setBaseVertex(outbuffer->getBaseVertex()+minIdx); + } + + outbuffer->setIndexType(newIdxType); + outbuffer->setIndexBufferBinding({ 0u, std::move(newIdxBuffer) }); + + if (verticesMustBeReordered) + { + auto* pipeline = outbuffer->getPipeline(); + + // reorder vertices according to index buffer +#define _ACCESS_IDX(n) ((newIdxType == EIT_32BIT) ? *(reinterpret_cast(indices)+(n)) : *(reinterpret_cast(indices)+(n))) + + const uint32_t posId = outbuffer->getPositionAttributeIx(); + const size_t bufsz = outbuffer->getAttribBoundBuffer(posId).buffer->getSize(); + + const size_t vertexSize = pipeline->getCachedCreationParams().vertexInput.bindings[0].stride; + uint8_t* const v = reinterpret_cast(outbuffer->getAttribBoundBuffer(posId).buffer->getPointer()); // after prefetch optim. we have guarantee of single vertex buffer so we can do like this + uint8_t* const vCopy = reinterpret_cast(_NBL_ALIGNED_MALLOC(bufsz, _NBL_SIMD_ALIGNMENT)); + memcpy(vCopy, v, bufsz); + + size_t baseVtx = outbuffer->getBaseVertex(); + for (size_t i = 0; i < outbuffer->getIndexCount(); ++i) + { + const uint32_t idx = _ACCESS_IDX(i+baseVtx); + if (idx != i+baseVtx) + memcpy(v + (vertexSize*(i + baseVtx)), vCopy + (vertexSize*idx), vertexSize); + } +#undef _ACCESS_IDX + _NBL_ALIGNED_FREE(vCopy); + } + } + + return outbuffer; +} + +void IMeshManipulator::requantizeMeshBuffer(ICPUMeshBuffer* _meshbuffer, const SErrorMetric* _errMetric) +{ + constexpr uint32_t MAX_ATTRIBS = ICPUMeshBuffer::MAX_VERTEX_ATTRIB_COUNT; + + CMeshManipulator::SAttrib newAttribs[MAX_ATTRIBS]; + for (size_t i = 0u; i < MAX_ATTRIBS; ++i) + newAttribs[i].vaid = i; + + CQuantNormalCache quantizationCache; + + core::unordered_map> attribsI; + core::unordered_map> attribsF; + for (size_t vaid = 0u; vaid < MAX_ATTRIBS; ++vaid) + { + const E_FORMAT type = _meshbuffer->getAttribFormat(vaid); + + const auto& vbuf = _meshbuffer->getAttribBoundBuffer(vaid).buffer; + if (_meshbuffer->isAttributeEnabled(vaid) && vbuf) + { + if (!isNormalizedFormat(type) && isIntegerFormat(type)) + attribsI[vaid] = CMeshManipulator::findBetterFormatI(&newAttribs[vaid].type, &newAttribs[vaid].size, &newAttribs[vaid].prevType, _meshbuffer, vaid, _errMetric[vaid]); + else + attribsF[vaid] = CMeshManipulator::findBetterFormatF(&newAttribs[vaid].type, &newAttribs[vaid].size, &newAttribs[vaid].prevType, _meshbuffer, vaid, _errMetric[vaid], quantizationCache); + } + } + + const size_t activeAttributeCount = attribsI.size() + attribsF.size(); + +#ifdef _NBL_DEBUG + { + core::unordered_set sizesSet; + for (core::unordered_map>::iterator it = attribsI.begin(); it != attribsI.end(); ++it) + sizesSet.insert(it->second.size()); + for (core::unordered_map>::iterator it = attribsF.begin(); it != attribsF.end(); ++it) + sizesSet.insert(it->second.size()); + _NBL_DEBUG_BREAK_IF(sizesSet.size() != 1); + } #endif -} // end namespace nbl::asset + const size_t vertexCnt = (!attribsI.empty() ? attribsI.begin()->second.size() : (!attribsF.empty() ? attribsF.begin()->second.size() : 0)); + + std::sort(newAttribs, newAttribs + MAX_ATTRIBS, std::greater()); // sort decreasing by size + + for (size_t i = 0u; i < activeAttributeCount; ++i) + { + const uint32_t typeSz = getTexelOrBlockBytesize(newAttribs[i].type); + const size_t alignment = (typeSz / getFormatChannelCount(newAttribs[i].type) == 8u) ? 8ull : 4ull; // if format 64bit per channel, than align to 8 + + newAttribs[i].offset = (i ? newAttribs[i - 1].offset + newAttribs[i - 1].size : 0u); + const size_t mod = newAttribs[i].offset % alignment; + newAttribs[i].offset += mod; + } + + const size_t vertexSize = newAttribs[activeAttributeCount - 1].offset + newAttribs[activeAttributeCount - 1].size; + + auto newVertexBuffer = ICPUBuffer::create({ vertexCnt * vertexSize }); + + constexpr uint32_t VTX_BUF_BINDING = 0u; + assert(_meshbuffer->getVertexBufferBindings()[0].buffer); + assert(_meshbuffer->isVertexAttribBufferBindingEnabled(VTX_BUF_BINDING)); + _meshbuffer->setVertexBufferBinding({ 0u, core::smart_refctd_ptr(newVertexBuffer) }, VTX_BUF_BINDING); + + auto* pipeline = _meshbuffer->getPipeline(); + auto& vtxParams = pipeline->getCachedCreationParams().vertexInput; + + vtxParams.bindings[VTX_BUF_BINDING].stride = vertexSize; + vtxParams.bindings[VTX_BUF_BINDING].inputRate = SVertexInputBindingParams::EVIR_PER_VERTEX; + for (size_t i = 0u; i < activeAttributeCount; ++i) + { + const uint32_t vaid = newAttribs[i].vaid; + vtxParams.attributes[vaid].binding = VTX_BUF_BINDING; + vtxParams.attributes[vaid].format = newAttribs[i].type; + vtxParams.attributes[vaid].relativeOffset = newAttribs[i].offset; + + core::unordered_map>::iterator iti = attribsI.find(newAttribs[i].vaid); + if (iti != attribsI.end()) + { + const core::vector& attrVec = iti->second; + for (size_t ai = 0u; ai < attrVec.size(); ++ai) + { + const bool check = _meshbuffer->setAttribute(attrVec[ai].pointer, newAttribs[i].vaid, ai); + _NBL_DEBUG_BREAK_IF(!check) + } + continue; + } + + core::unordered_map>::iterator itf = attribsF.find(newAttribs[i].vaid); + if (itf != attribsF.end()) + { + const core::vector& attrVec = itf->second; + for (size_t ai = 0u; ai < attrVec.size(); ++ai) + { + const bool check = _meshbuffer->setAttribute(attrVec[ai], newAttribs[i].vaid, ai); + _NBL_DEBUG_BREAK_IF(!check) + } + } + } +} + + + +void IMeshManipulator::filterInvalidTriangles(ICPUMeshBuffer* _input) +{ + if (!_input || !_input->getPipeline() || !_input->getIndices()) + return; + + switch (_input->getIndexType()) + { + case EIT_16BIT: + return CMeshManipulator::_filterInvalidTriangles(_input); + case EIT_32BIT: + return CMeshManipulator::_filterInvalidTriangles(_input); + default: return; + } +} + +template +void CMeshManipulator::_filterInvalidTriangles(ICPUMeshBuffer* _input) +{ + const size_t size = _input->getIndexCount() * sizeof(IdxT); + void* const copy = _NBL_ALIGNED_MALLOC(size,_NBL_SIMD_ALIGNMENT); + memcpy(copy, _input->getIndices(), size); + + struct Triangle + { + IdxT i[3]; + } *const begin = (Triangle*)copy, *const end = (Triangle*)(reinterpret_cast(copy) + size); + + Triangle* const newEnd = std::remove_if(begin, end, + [&_input](const Triangle& _t) { + core::vectorSIMDf p0, p1, p2; + const uint32_t pvaid = _input->getPositionAttributeIx(); + _input->getAttribute(p0, pvaid, _t.i[0]); + _input->getAttribute(p1, pvaid, _t.i[1]); + _input->getAttribute(p2, pvaid, _t.i[2]); + return core::length(core::cross(p1 - p0, p2 - p0)).x<=1.0e-19F; + }); + const size_t newSize = std::distance(begin, newEnd) * sizeof(Triangle); + + auto newBuf = ICPUBuffer::create({ newSize }); + memcpy(newBuf->getPointer(), copy, newSize); + _NBL_ALIGNED_FREE(copy); + + SBufferBinding idxBufBinding; + idxBufBinding.offset = 0ull; + idxBufBinding.buffer = std::move(newBuf); + _input->setIndexBufferBinding(std::move(idxBufBinding)); + _input->setIndexCount(newSize/sizeof(IdxT)); +} +template void CMeshManipulator::_filterInvalidTriangles(ICPUMeshBuffer* _input); +template void CMeshManipulator::_filterInvalidTriangles(ICPUMeshBuffer* _input); + +core::vector CMeshManipulator::findBetterFormatF(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric, CQuantNormalCache& _cache) +{ + if (!_meshbuffer->getPipeline()) + return {}; + + const E_FORMAT thisType = _meshbuffer->getAttribFormat(_attrId); + + if (!isFloatingPointFormat(thisType) && !isNormalizedFormat(thisType) && !isScaledFormat(thisType)) + return {}; + + core::vector attribs; + + + const uint32_t cpa = getFormatChannelCount(thisType); + + float min[4]{ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + float max[4]{ -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX }; + + core::vectorSIMDf attr; + const uint32_t cnt = IMeshManipulator::upperBoundVertexID(_meshbuffer); + for (uint32_t idx = 0u; idx < cnt; ++idx) + { + _meshbuffer->getAttribute(attr, _attrId, idx); + attribs.push_back(attr); + for (uint32_t i = 0; i < cpa ; ++i) + { + if (attr.pointer[i] < min[i]) + min[i] = attr.pointer[i]; + if (attr.pointer[i] > max[i]) + max[i] = attr.pointer[i]; + } + } + + core::vector possibleTypes = findTypesOfProperRangeF(thisType, getTexelOrBlockBytesize(thisType), min, max, _errMetric); + std::sort(possibleTypes.begin(), possibleTypes.end(), [](const SAttribTypeChoice& t1, const SAttribTypeChoice& t2) { return getTexelOrBlockBytesize(t1.type) < getTexelOrBlockBytesize(t2.type); }); + + *_outPrevType = thisType; + *_outType = thisType; + *_outSize = getTexelOrBlockBytesize(*_outType); + + for (const SAttribTypeChoice& t : possibleTypes) + { + if (calcMaxQuantizationError({ thisType }, t, attribs, _errMetric, _cache)) + { + if (getTexelOrBlockBytesize(t.type) < getTexelOrBlockBytesize(thisType)) + { + *_outType = t.type; + *_outSize = getTexelOrBlockBytesize(*_outType); + } + + return attribs; + } + } + + return attribs; +} + +core::vector CMeshManipulator::findBetterFormatI(E_FORMAT* _outType, size_t* _outSize, E_FORMAT* _outPrevType, const ICPUMeshBuffer* _meshbuffer, uint32_t _attrId, const SErrorMetric& _errMetric) +{ + if (!_meshbuffer->getPipeline()) + return {}; + + const E_FORMAT thisType = _meshbuffer->getAttribFormat(_attrId); + + if (!isIntegerFormat(thisType)) + return {}; + + if (isBGRALayoutFormat(thisType)) + return {}; // BGRA is supported only by a few normalized types (this is function for integer types) + + core::vector attribs; + + + const uint32_t cpa = getFormatChannelCount(thisType); + + uint32_t min[4]; + uint32_t max[4]; + if (!isSignedFormat(thisType)) + for (size_t i = 0; i < 4; ++i) + min[i] = UINT_MAX; + else + for (size_t i = 0; i < 4; ++i) + min[i] = INT_MAX; + if (!isSignedFormat(thisType)) + for (size_t i = 0; i < 4; ++i) + max[i] = 0; + else + for (size_t i = 0; i < 4; ++i) + max[i] = INT_MIN; + + + SIntegerAttr attr; + const uint32_t cnt = IMeshManipulator::upperBoundVertexID(_meshbuffer); + for (uint32_t idx = 0u; idx < cnt; ++idx) + { + _meshbuffer->getAttribute(attr.pointer, _attrId, idx); + attribs.push_back(attr); + for (uint32_t i = 0; i < cpa; ++i) + { + if (!isSignedFormat(thisType)) + { + if (attr.pointer[i] < min[i]) + min[i] = attr.pointer[i]; + if (attr.pointer[i] > max[i]) + max[i] = attr.pointer[i]; + } + else + { + if (((int32_t*)attr.pointer + i)[0] < ((int32_t*)min + i)[0]) + min[i] = attr.pointer[i]; + if (((int32_t*)attr.pointer + i)[0] > ((int32_t*)max + i)[0]) + max[i] = attr.pointer[i]; + } + } + } + + *_outPrevType = *_outType = thisType; + *_outSize = getTexelOrBlockBytesize(thisType); + *_outPrevType = thisType; + + if (_errMetric.method == EEM_ANGLES) // native integers normals does not change + return attribs; + + *_outType = getBestTypeI(thisType, _outSize, min, max); + if (getTexelOrBlockBytesize(*_outType) >= getTexelOrBlockBytesize(thisType)) + { + *_outType = thisType; + *_outSize = getTexelOrBlockBytesize(thisType); + } + return attribs; +} + +E_FORMAT CMeshManipulator::getBestTypeI(E_FORMAT _originalType, size_t* _outSize, const uint32_t* _min, const uint32_t* _max) +{ + using namespace video; + + const bool isNativeInteger = isIntegerFormat(_originalType); + const bool isUnsigned = !isSignedFormat(_originalType); + + const uint32_t originalCpa = getFormatChannelCount(_originalType); + + core::vector nativeInts{ + EF_R8G8_UINT, + EF_R8G8_SINT, + EF_R8G8B8_UINT, + EF_R8G8B8_SINT, + EF_R8G8B8A8_UINT, + EF_R8G8B8A8_SINT, + EF_A2B10G10R10_UINT_PACK32, + EF_A2B10G10R10_SINT_PACK32, + EF_R16_UINT, + EF_R16_SINT, + EF_R16G16_UINT, + EF_R16G16_SINT, + EF_R16G16B16_UINT, + EF_R16G16B16_SINT, + EF_R16G16B16A16_UINT, + EF_R16G16B16A16_SINT, + EF_R32_UINT, + EF_R32_SINT, + EF_R32G32_UINT, + EF_R32G32_SINT, + EF_R32G32B32_UINT, + EF_R32G32B32_SINT, + EF_R32G32B32A32_UINT, + EF_R32G32B32A32_SINT + }; + core::vector scaledInts{ + EF_R8G8_USCALED, + EF_R8G8_SSCALED, + EF_R8G8B8_USCALED, + EF_R8G8B8_SSCALED, + EF_R8G8B8A8_USCALED, + EF_R8G8B8A8_SSCALED, + EF_A2B10G10R10_USCALED_PACK32, + EF_A2B10G10R10_SSCALED_PACK32, + EF_R16_USCALED, + EF_R16_SSCALED, + EF_R16G16_USCALED, + EF_R16G16_SSCALED, + EF_R16G16B16_USCALED, + EF_R16G16B16_SSCALED, + EF_R16G16B16A16_USCALED, + EF_R16G16B16A16_SSCALED + }; + + core::vector& all = isNativeInteger ? nativeInts : scaledInts; + if (originalCpa > 1u) + { + all.erase( + std::remove_if(all.begin(), all.end(), + [originalCpa](E_FORMAT fmt) { return getFormatChannelCount(fmt) < originalCpa; } + ), + all.end() + ); + } + auto minValueOfTypeINT = [](E_FORMAT _fmt, uint32_t _cmpntNum) -> int32_t { + if (!isSignedFormat(_fmt)) + return 0; + + switch (_fmt) + { + case EF_A2R10G10B10_SSCALED_PACK32: + case EF_A2R10G10B10_SINT_PACK32: + case EF_A2B10G10R10_SSCALED_PACK32: + case EF_A2B10G10R10_SINT_PACK32: + if (_cmpntNum < 3u) + return -512; + else return -2; + break; + default: + { + const uint32_t bitsPerCh = getTexelOrBlockBytesize(_fmt)*8u/getFormatChannelCount(_fmt); + return int32_t(-uint64_t(1ull<<(bitsPerCh-1u))); + } + } + }; + auto maxValueOfTypeINT = [](E_FORMAT _fmt, uint32_t _cmpntNum) -> uint32_t { + switch (_fmt) + { + case EF_A2R10G10B10_USCALED_PACK32: + case EF_A2R10G10B10_UINT_PACK32: + case EF_A2B10G10R10_USCALED_PACK32: + case EF_A2B10G10R10_UINT_PACK32: + if (_cmpntNum < 3u) + return 1023u; + else return 3u; + break; + case EF_A2R10G10B10_SSCALED_PACK32: + case EF_A2R10G10B10_SINT_PACK32: + case EF_A2B10G10R10_SSCALED_PACK32: + case EF_A2B10G10R10_SINT_PACK32: + if (_cmpntNum < 3u) + return 511u; + else return 1u; + break; + default: + { + const uint32_t bitsPerCh = getTexelOrBlockBytesize(_fmt)*8u/getFormatChannelCount(_fmt); + const uint64_t r = (1ull<>1); + } + } + }; + + E_FORMAT bestType = _originalType; + for (auto it = all.begin(); it != all.end(); ++it) + { + bool ok = true; + for (uint32_t cmpntNum = 0; cmpntNum < originalCpa; ++cmpntNum) // check only `_cpa` components because even if (chosenCpa > _cpa), we don't care about extra components + { + if (isUnsigned) + { + if (!(_min[cmpntNum] >= minValueOfTypeINT(*it, cmpntNum) && _max[cmpntNum] <= maxValueOfTypeINT(*it, cmpntNum))) //! TODO: FIX signed vs. unsigned comparison + { + ok = false; + break; + } + } + else + { + if (!(((int32_t*)(_min + cmpntNum))[0] >= minValueOfTypeINT(*it, cmpntNum) && ((int32_t*)(_max + cmpntNum))[0] <= maxValueOfTypeINT(*it, cmpntNum))) //! TODO: FIX signed vs. unsigned comparison + { + ok = false; + break; + } + } + } + if (ok && getTexelOrBlockBytesize(*it) < getTexelOrBlockBytesize(bestType)) // vertexAttrSize array defined in IMeshBuffer.h + { + bestType = *it; + *_outSize = getTexelOrBlockBytesize(bestType); + } + } + + return bestType; +} + +core::vector CMeshManipulator::findTypesOfProperRangeF(E_FORMAT _type, size_t _sizeThreshold, const float * _min, const float * _max, const SErrorMetric& _errMetric) +{ + using namespace video; + + core::vector all{ + EF_B10G11R11_UFLOAT_PACK32, + EF_R16_SFLOAT, + EF_R16G16_SFLOAT, + EF_R16G16B16_SFLOAT, + EF_R16G16B16A16_SFLOAT, + EF_R32_SFLOAT, + EF_R32G32_SFLOAT, + EF_R32G32B32_SFLOAT, + EF_R32G32B32A32_SFLOAT, + EF_R8G8_UNORM, + EF_R8G8_SNORM, + EF_R8G8B8_UNORM, + EF_R8G8B8_SNORM, + EF_B8G8R8A8_UNORM, //bgra + EF_R8G8B8A8_UNORM, + EF_R8G8B8A8_SNORM, + EF_A2B10G10R10_UNORM_PACK32, + EF_A2B10G10R10_SNORM_PACK32, + EF_A2R10G10B10_UNORM_PACK32, //bgra + EF_A2R10G10B10_SNORM_PACK32, //bgra + EF_R16_UNORM, + EF_R16_SNORM, + EF_R16G16_UNORM, + EF_R16G16_SNORM, + EF_R16G16B16_UNORM, + EF_R16G16B16_SNORM, + EF_R16G16B16A16_UNORM, + EF_R16G16B16A16_SNORM + }; + core::vector normalized{ + EF_B8G8R8A8_UNORM, //bgra + EF_R8G8B8A8_UNORM, + EF_R8G8B8A8_SNORM, + EF_A2B10G10R10_UNORM_PACK32, + EF_A2B10G10R10_SNORM_PACK32, + EF_A2R10G10B10_UNORM_PACK32, //bgra + EF_A2R10G10B10_SNORM_PACK32, //bgra + EF_R16_UNORM, + EF_R16_SNORM, + EF_R16G16_UNORM, + EF_R16G16_SNORM, + EF_R16G16B16_UNORM, + EF_R16G16B16_SNORM, + EF_R16G16B16A16_UNORM, + EF_R16G16B16A16_SNORM + }; + core::vector bgra{ + EF_B8G8R8A8_UNORM, //bgra + EF_A2R10G10B10_UNORM_PACK32, //bgra + EF_A2R10G10B10_SNORM_PACK32, //bgra + }; + core::vector normals{ + EF_R8_SNORM, + EF_R8G8_SNORM, + EF_R8G8B8_SNORM, + EF_R8G8B8A8_SNORM, + EF_R16_SNORM, + EF_R16G16_SNORM, + EF_R16G16B16_SNORM, + EF_R16G16B16A16_SNORM, + EF_A2B10G10R10_SNORM_PACK32, + EF_A2R10G10B10_SNORM_PACK32, //bgra + EF_R16_SFLOAT, + EF_R16G16_SFLOAT, + EF_R16G16B16_SFLOAT, + EF_R16G16B16A16_SFLOAT + }; + + auto minValueOfTypeFP = [](E_FORMAT _fmt, uint32_t _cmpntNum) -> float { + if (isNormalizedFormat(_fmt)) + { + return isSignedFormat(_fmt) ? -1.f : 0.f; + } + switch (_fmt) + { + case EF_R16_SFLOAT: + case EF_R16G16_SFLOAT: + case EF_R16G16B16_SFLOAT: + case EF_R16G16B16A16_SFLOAT: + return -65504.f; + case EF_R32_SFLOAT: + case EF_R32G32_SFLOAT: + case EF_R32G32B32_SFLOAT: + case EF_R32G32B32A32_SFLOAT: + return -FLT_MAX; + case EF_B10G11R11_UFLOAT_PACK32: + return 0.f; + default: + return 1.f; + } + }; + auto maxValueOfTypeFP = [](E_FORMAT _fmt, uint32_t _cmpntNum) -> float { + if (isNormalizedFormat(_fmt)) + { + return 1.f; + } + switch (_fmt) + { + case EF_R16_SFLOAT: + case EF_R16G16_SFLOAT: + case EF_R16G16B16_SFLOAT: + case EF_R16G16B16A16_SFLOAT: + return 65504.f; + case EF_R32_SFLOAT: + case EF_R32G32_SFLOAT: + case EF_R32G32B32_SFLOAT: + case EF_R32G32B32A32_SFLOAT: + return FLT_MAX; + case EF_B10G11R11_UFLOAT_PACK32: + if (_cmpntNum < 2u) + return 65024.f; + else return 64512.f; + default: + return 0.f; + } + }; + + if (isNormalizedFormat(_type) || _errMetric.method == EEM_ANGLES) + { + if (_errMetric.method == EEM_ANGLES) + { + if (isBGRALayoutFormat(_type)) + { + all = core::vector(1u, EF_A2R10G10B10_SNORM_PACK32); + } + else all = std::move(normals); + } + else if (isBGRALayoutFormat(_type)) + all = std::move(bgra); + else + all = std::move(normalized); + } + + if (isNormalizedFormat(_type) && !isSignedFormat(_type)) + all.erase(std::remove_if(all.begin(), all.end(), [](E_FORMAT _t) { return isSignedFormat(_t); }), all.end()); + else if (isNormalizedFormat(_type) && isSignedFormat(_type)) + all.erase(std::remove_if(all.begin(), all.end(), [](E_FORMAT _t) { return !isSignedFormat(_t); }), all.end()); + + const uint32_t originalCpa = getFormatChannelCount(_type); + all.erase( + std::remove_if(all.begin(), all.end(), + [originalCpa](E_FORMAT fmt) { return getFormatChannelCount(fmt) < originalCpa; } + ), + all.end() + ); + + core::vector possibleTypes; + core::vectorSIMDf min(_min), max(_max); + + for (auto it = all.begin(); it != all.end(); ++it) + { + bool ok = true; + for (uint32_t cmpntNum = 0; cmpntNum < originalCpa; ++cmpntNum) // check only `_cpa` components because even if (chosenCpa > _cpa), we don't care about extra components + { + if (!(min.pointer[cmpntNum] >= minValueOfTypeFP(*it, cmpntNum) && max.pointer[cmpntNum] <= maxValueOfTypeFP(*it, cmpntNum))) + { + ok = false; + break; // break loop comparing (*it)'s range component by component + } + } + if (ok && getTexelOrBlockBytesize(*it) <= _sizeThreshold) + possibleTypes.push_back({*it}); + } + return possibleTypes; +} + +bool CMeshManipulator::calcMaxQuantizationError(const SAttribTypeChoice& _srcType, const SAttribTypeChoice& _dstType, const core::vector& _srcData, const SErrorMetric& _errMetric, CQuantNormalCache& _cache) +{ + using namespace video; + + using QuantF_t = core::vectorSIMDf(*)(const core::vectorSIMDf&, E_FORMAT, E_FORMAT, CQuantNormalCache & _cache); + + QuantF_t quantFunc = nullptr; + + if (_errMetric.method == EEM_ANGLES) + { + switch (_dstType.type) + { + case EF_R8_SNORM: + case EF_R8G8_SNORM: + case EF_R8G8B8_SNORM: + case EF_R8G8B8A8_SNORM: + quantFunc = [](const core::vectorSIMDf& _in, E_FORMAT, E_FORMAT, CQuantNormalCache& _cache) -> core::vectorSIMDf { + uint8_t buf[32]; + ((CQuantNormalCache::value_type_t*)buf)[0] = _cache.quantize(_in); + + core::vectorSIMDf retval; + ICPUMeshBuffer::getAttribute(retval, buf, EF_R8G8B8A8_SNORM); + retval.w = 1.f; + return retval; + }; + break; + case EF_A2R10G10B10_SNORM_PACK32: + case EF_A2B10G10R10_SNORM_PACK32: // bgra + quantFunc = [](const core::vectorSIMDf& _in, E_FORMAT, E_FORMAT, CQuantNormalCache& _cache) -> core::vectorSIMDf { + uint8_t buf[32]; + ((CQuantNormalCache::value_type_t*)buf)[0] = _cache.quantize(_in); + + core::vectorSIMDf retval; + ICPUMeshBuffer::getAttribute(retval, buf, EF_A2R10G10B10_SNORM_PACK32); + retval.w = 1.f; + return retval; + }; + break; + case EF_R16_SNORM: + case EF_R16G16_SNORM: + case EF_R16G16B16_SNORM: + case EF_R16G16B16A16_SNORM: + quantFunc = [](const core::vectorSIMDf& _in, E_FORMAT, E_FORMAT, CQuantNormalCache& _cache) -> core::vectorSIMDf { + uint8_t buf[32]; + ((CQuantNormalCache::value_type_t*)buf)[0] = _cache.quantize(_in); + + core::vectorSIMDf retval; + ICPUMeshBuffer::getAttribute(retval, buf, EF_R16G16B16A16_SNORM); + retval.w = 1.f; + return retval; + }; + break; + default: + quantFunc = nullptr; + break; + } + } + else + { + quantFunc = [](const core::vectorSIMDf& _in, E_FORMAT _inType, E_FORMAT _outType, CQuantNormalCache& _cache) -> core::vectorSIMDf { + uint8_t buf[32]; + ICPUMeshBuffer::setAttribute(_in, buf, _outType); + core::vectorSIMDf out(0.f, 0.f, 0.f, 1.f); + ICPUMeshBuffer::getAttribute(out, buf, _outType); + return out; + }; + } + + _NBL_DEBUG_BREAK_IF(!quantFunc) + if (!quantFunc) + return false; + + for (const core::vectorSIMDf& d : _srcData) + { + const core::vectorSIMDf quantized = quantFunc(d, _srcType.type, _dstType.type, _cache); + if (!compareFloatingPointAttribute(d, quantized, getFormatChannelCount(_srcType.type), _errMetric)) + return false; + } + + return true; +} + +core::smart_refctd_ptr IMeshManipulator::idxBufferFromLineStripsToLines(const void* _input, uint32_t& _idxCount, E_INDEX_TYPE _inIndexType, E_INDEX_TYPE _outIndexType) +{ + if (_inIndexType == EIT_16BIT) + { + if (_outIndexType == EIT_16BIT) + return CMeshManipulator::lineStripsToLines(_input, _idxCount); + else + return CMeshManipulator::lineStripsToLines(_input, _idxCount); + } + else if (_inIndexType == EIT_32BIT) + { + if (_outIndexType == EIT_16BIT) + return CMeshManipulator::lineStripsToLines(_input, _idxCount); + else + return CMeshManipulator::lineStripsToLines(_input, _idxCount); + } + return nullptr; +} + +core::smart_refctd_ptr IMeshManipulator::idxBufferFromTriangleStripsToTriangles(const void* _input, uint32_t& _idxCount, E_INDEX_TYPE _inIndexType, E_INDEX_TYPE _outIndexType) +{ + if (_inIndexType == EIT_16BIT) + { + if (_outIndexType == EIT_16BIT) + return CMeshManipulator::triangleStripsToTriangles(_input, _idxCount); + else + return CMeshManipulator::triangleStripsToTriangles(_input, _idxCount); + } + else if (_inIndexType == EIT_32BIT) + { + if (_outIndexType == EIT_16BIT) + return CMeshManipulator::triangleStripsToTriangles(_input, _idxCount); + else + return CMeshManipulator::triangleStripsToTriangles(_input, _idxCount); + } + return nullptr; +} + +core::smart_refctd_ptr IMeshManipulator::idxBufferFromTrianglesFanToTriangles(const void* _input, uint32_t& _idxCount, E_INDEX_TYPE _inIndexType, E_INDEX_TYPE _outIndexType) +{ + if (_inIndexType == EIT_16BIT) + { + if (_outIndexType == EIT_16BIT) + return CMeshManipulator::trianglesFanToTriangles(_input, _idxCount); + else + return CMeshManipulator::trianglesFanToTriangles(_input, _idxCount); + } + else if (_inIndexType == EIT_32BIT) + { + if (_outIndexType == EIT_16BIT) + return CMeshManipulator::trianglesFanToTriangles(_input, _idxCount); + else + return CMeshManipulator::trianglesFanToTriangles(_input, _idxCount); + } + return nullptr; +} + +float IMeshManipulator::DistanceToLine(core::vectorSIMDf P0, core::vectorSIMDf P1, core::vectorSIMDf InPoint) +{ + core::vectorSIMDf PointToStart = InPoint - P0; + core::vectorSIMDf Diff = core::cross(P0 - P1, PointToStart); + + return core::dot(Diff, Diff).x; +} + +float IMeshManipulator::DistanceToPlane(core::vectorSIMDf InPoint, core::vectorSIMDf PlanePoint, core::vectorSIMDf PlaneNormal) +{ + core::vectorSIMDf PointToPlane = InPoint - PlanePoint; + + return (core::dot(PointToPlane, PlaneNormal).x >= 0) ? core::abs(core::dot(PointToPlane, PlaneNormal).x) : 0; +} + +#endif +} // end namespace nbl::asset diff --git a/src/nbl/asset/utils/CSPIRVIntrospector.cpp b/src/nbl/asset/utils/CSPIRVIntrospector.cpp index 4ac78066a7..818fbc584b 100644 --- a/src/nbl/asset/utils/CSPIRVIntrospector.cpp +++ b/src/nbl/asset/utils/CSPIRVIntrospector.cpp @@ -1054,7 +1054,7 @@ void CSPIRVIntrospector::CStageIntrospectionData::debugPrint(system::ILogger* lo } } - logger->log(debug.str() + '\n'); + logger->log("%s", system::ILogger::ELL_DEBUG, debug.str() + '\n'); } } \ No newline at end of file diff --git a/src/nbl/asset/utils/CSmoothNormalGenerator.cpp b/src/nbl/asset/utils/CSmoothNormalGenerator.cpp index 43413152a8..f8bc45a317 100644 --- a/src/nbl/asset/utils/CSmoothNormalGenerator.cpp +++ b/src/nbl/asset/utils/CSmoothNormalGenerator.cpp @@ -5,7 +5,7 @@ #include "CSmoothNormalGenerator.h" #include "nbl/core/declarations.h" -#include "nbl/builtin/hlsl/shapes/spherical_triangle.hlsl" +#include "nbl/builtin/hlsl/shapes/triangle.hlsl" #include @@ -58,7 +58,7 @@ CSmoothNormalGenerator::VertexHashMap CSmoothNormalGenerator::setupData(const as const auto faceNormal = normalize(cross(v1 - v0, v2 - v0)); //set data for m_vertices - const auto angleWages = hlsl::shapes::util::compInternalAngle(v2 - v1, v0 - v2, v1 - v2); + const auto angleWages = hlsl::shapes::util::anglesFromTriangleEdges(v2 - v1, v0 - v2, v1 - v2); vertices.add({ i, 0, faceNormal * angleWages.x, v0}); vertices.add({ i + 1, 0, faceNormal * angleWages.y,v1}); diff --git a/src/nbl/asset/utils/CWaveStringResolver.cpp b/src/nbl/asset/utils/CWaveStringResolver.cpp index a2165972e5..d49ca1ac32 100644 --- a/src/nbl/asset/utils/CWaveStringResolver.cpp +++ b/src/nbl/asset/utils/CWaveStringResolver.cpp @@ -58,15 +58,21 @@ namespace nbl::wave // now define them as "NBL_GLSL_LIMIT_MAX_IMAGE_DIMENSION_1D=32768" // to match boost wave syntax // https://www.boost.org/doc/libs/1_82_0/libs/wave/doc/class_reference_context.html#:~:text=Maintain%20defined%20macros-,add_macro_definition,-bool%20add_macro_definition - for (const auto& define : preprocessOptions.extraDefines) - context.add_macro_definition(define.identifier.data() + core::string("=") + define.definition.data()); // preprocess core::string resolvedString; try { + for (const auto& define : preprocessOptions.extraDefines) + { + std::string macroDefinition(define.identifier); + macroDefinition.push_back('='); + macroDefinition.append(define.definition); + context.add_macro_definition(macroDefinition); + } + auto stream = std::stringstream(); - for (auto i= context.begin(); i!= context.end(); i++) + for (auto i = context.begin(); i != context.end(); i++) stream << i->get_value(); resolvedString = stream.str(); } @@ -85,4 +91,4 @@ namespace nbl::wave return resolvedString; } -} \ No newline at end of file +} diff --git a/src/nbl/asset/utils/IShaderCompiler.cpp b/src/nbl/asset/utils/IShaderCompiler.cpp index e60bf31b5c..a6cd95b441 100644 --- a/src/nbl/asset/utils/IShaderCompiler.cpp +++ b/src/nbl/asset/utils/IShaderCompiler.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -21,40 +23,287 @@ IShaderCompiler::IShaderCompiler(core::smart_refctd_ptr&& syste m_defaultIncludeFinder = core::make_smart_refctd_ptr(core::smart_refctd_ptr(m_system)); } -core::smart_refctd_ptr nbl::asset::IShaderCompiler::compileToSPIRV(const std::string_view code, const SCompilerOptions& options) const +bool IShaderCompiler::writeDepfile( + const DepfileWriteParams& params, + const CCache::SEntry::dependency_container_t& dependencies, + const CIncludeFinder* includeFinder, + system::logger_opt_ptr logger) { - CCache::SEntry entry; - if (options.readCache || options.writeCache) - entry = CCache::SEntry(code, options); - - if (options.readCache) - { - auto found = options.readCache->find_impl(entry, options.preprocessorOptions.includeFinder); - if (found != options.readCache->m_container.end()) - { - if (options.writeCache) - { - CCache::SEntry writeEntry = *found; - options.writeCache->insert(std::move(writeEntry)); - } - return found->decompressShader(); - } - } - - auto retVal = compileToSPIRV_impl(code, options, options.writeCache ? &entry.dependencies:nullptr); - // compute the SPIR-V shader content hash - if (retVal) - { - auto backingBuffer = retVal->getContent(); - const_cast(backingBuffer)->setContentHash(backingBuffer->computeContentHash()); - } + std::string depfilePathString; + if (!params.depfilePath.empty()) + depfilePathString = std::string(params.depfilePath); + else + depfilePathString = std::string(params.outputPath) + ".d"; + + if (depfilePathString.empty()) + { + logger.log("Depfile path is empty.", system::ILogger::ELL_ERROR); + return false; + } + + const auto parentDirectory = std::filesystem::path(depfilePathString).parent_path(); + if (!parentDirectory.empty() && !std::filesystem::exists(parentDirectory)) + { + if (!std::filesystem::create_directories(parentDirectory)) + { + logger.log("Failed to create parent directory for depfile.", system::ILogger::ELL_ERROR); + return false; + } + } + + std::vector depPaths; + depPaths.reserve(dependencies.size() + 1); + + auto addDepPath = [&depPaths, ¶ms](std::filesystem::path path) + { + if (path.empty()) + return; + if (path.is_relative()) + { + if (params.workingDirectory.empty()) + return; + path = std::filesystem::path(params.workingDirectory) / path; + } + std::error_code ec; + std::filesystem::path normalized = std::filesystem::weakly_canonical(path, ec); + if (ec) + { + normalized = std::filesystem::absolute(path, ec); + if (ec) + return; + } + if (normalized.empty() || !std::filesystem::exists(normalized)) + return; + auto normalizedString = normalized.generic_string(); + if (normalizedString.find_first_of("\r\n") != std::string::npos) + return; + depPaths.emplace_back(std::move(normalizedString)); + }; + + if (!params.sourceIdentifier.empty()) + { + std::filesystem::path rootPath{std::string(params.sourceIdentifier)}; + if (rootPath.is_relative()) + { + if (!params.workingDirectory.empty()) + rootPath = std::filesystem::absolute(std::filesystem::path(params.workingDirectory) / rootPath); + else + rootPath = std::filesystem::absolute(rootPath); + } + addDepPath(rootPath); + } + + for (const auto& dep : dependencies) + { + if (includeFinder) + { + IShaderCompiler::IIncludeLoader::found_t header = dep.isStandardInclude() ? + includeFinder->getIncludeStandard(dep.getRequestingSourceDir(), std::string(dep.getIdentifier())) : + includeFinder->getIncludeRelative(dep.getRequestingSourceDir(), std::string(dep.getIdentifier())); + + if (!header) + continue; + addDepPath(header.absolutePath); + } + else + { + std::filesystem::path candidate = dep.isStandardInclude() ? std::filesystem::path(std::string(dep.getIdentifier())) : (dep.getRequestingSourceDir() / std::string(dep.getIdentifier())); + if (candidate.is_relative()) + { + if (!params.workingDirectory.empty()) + candidate = std::filesystem::absolute(std::filesystem::path(params.workingDirectory) / candidate); + else + candidate = std::filesystem::absolute(candidate); + } + addDepPath(candidate); + } + } + + std::sort(depPaths.begin(), depPaths.end()); + depPaths.erase(std::unique(depPaths.begin(), depPaths.end()), depPaths.end()); + + auto escapeDepPath = [](const std::string& path) -> std::string + { + std::string normalized = path; + std::replace(normalized.begin(), normalized.end(), '\\', '/'); + std::string out; + out.reserve(normalized.size()); + for (const char c : normalized) + { + if (c == ' ' || c == '#') + out.push_back('\\'); + if (c == '$') + { + out.push_back('$'); + out.push_back('$'); + continue; + } + out.push_back(c); + } + return out; + }; + + if (!params.system) + { + logger.log("Depfile system is null.", system::ILogger::ELL_ERROR); + return false; + } + + const auto depfilePath = std::filesystem::path(depfilePathString); + auto tempPath = depfilePath; + tempPath += ".tmp"; + params.system->deleteFile(tempPath); + + core::smart_refctd_ptr depfile; + { + system::ISystem::future_t> future; + params.system->createFile(future, tempPath, system::IFileBase::ECF_WRITE); + if (!future.wait()) + { + logger.log("Failed to open depfile: %s", system::ILogger::ELL_ERROR, depfilePathString.c_str()); + return false; + } + future.acquire().move_into(depfile); + } + if (!depfile) + { + logger.log("Failed to open depfile: %s", system::ILogger::ELL_ERROR, depfilePathString.c_str()); + return false; + } + + std::string targetPathString; + if (params.outputPath.empty()) + { + std::filesystem::path targetPath = depfilePathString; + if (targetPath.extension() == ".d") + targetPath.replace_extension(); + targetPathString = targetPath.generic_string(); + } + else + { + targetPathString = std::string(params.outputPath); + } + if (targetPathString.empty()) + { + logger.log("Depfile target path is empty.", system::ILogger::ELL_ERROR); + return false; + } + const std::string target = escapeDepPath(std::filesystem::path(targetPathString).generic_string()); + std::vector escapedDeps; + escapedDeps.reserve(depPaths.size()); + for (const auto& depPath : depPaths) + escapedDeps.emplace_back(escapeDepPath(depPath)); + + std::string depfileContents; + depfileContents.append(target); + depfileContents.append(":"); + if (!escapedDeps.empty()) + { + depfileContents.append(" \\\n"); + for (size_t index = 0; index < escapedDeps.size(); ++index) + { + depfileContents.append(" "); + depfileContents.append(escapedDeps[index]); + if (index + 1 < escapedDeps.size()) + depfileContents.append(" \\\n"); + } + } + depfileContents.append("\n"); + + system::IFile::success_t success; + depfile->write(success, depfileContents.data(), 0, depfileContents.size()); + if (!success) + { + logger.log("Failed to write depfile: %s", system::ILogger::ELL_ERROR, depfilePathString.c_str()); + return false; + } + depfile = nullptr; + + params.system->deleteFile(depfilePath); + const std::error_code moveError = params.system->moveFileOrDirectory(tempPath, depfilePath); + if (moveError) + { + logger.log("Failed to replace depfile: %s", system::ILogger::ELL_ERROR, depfilePathString.c_str()); + return false; + } + return true; +} - if (options.writeCache) - { - if (entry.setContent(retVal->getContent())) - options.writeCache->insert(std::move(entry)); - } - return retVal; +core::smart_refctd_ptr nbl::asset::IShaderCompiler::compileToSPIRV(const std::string_view code, const SCompilerOptions& options) const +{ + const bool depfileEnabled = options.preprocessorOptions.depfile; + const bool supportsDependencies = options.getCodeContentType() == IShader::E_CONTENT_TYPE::ECT_HLSL; + + auto writeDepfileFromDependencies = [&](const CCache::SEntry::dependency_container_t& dependencies) -> bool + { + if (!depfileEnabled) + return true; + + if (options.preprocessorOptions.depfilePath.empty()) + { + options.preprocessorOptions.logger.log("Depfile path is empty.", system::ILogger::ELL_ERROR); + return false; + } + + IShaderCompiler::DepfileWriteParams params = {}; + const std::string depfilePathString = options.preprocessorOptions.depfilePath.generic_string(); + params.depfilePath = depfilePathString; + params.sourceIdentifier = options.preprocessorOptions.sourceIdentifier; + if (!params.sourceIdentifier.empty()) + params.workingDirectory = std::filesystem::path(std::string(params.sourceIdentifier)).parent_path(); + params.system = m_system.get(); + return IShaderCompiler::writeDepfile(params, dependencies, options.preprocessorOptions.includeFinder, options.preprocessorOptions.logger); + }; + + CCache::SEntry entry; + if (options.readCache || options.writeCache) + entry = CCache::SEntry(code, options); + + if (options.readCache) + { + auto found = options.readCache->find_impl(entry, options.preprocessorOptions.includeFinder); + if (found != options.readCache->m_container.end()) + { + if (options.writeCache) + { + CCache::SEntry writeEntry = *found; + options.writeCache->insert(std::move(writeEntry)); + } + auto shader = found->decompressShader(); + if (depfileEnabled && !writeDepfileFromDependencies(found->dependencies)) + return nullptr; + return shader; + } + } + + CCache::SEntry::dependency_container_t depfileDependencies; + CCache::SEntry::dependency_container_t* dependenciesPtr = nullptr; + if (options.writeCache) + dependenciesPtr = &entry.dependencies; + else if (depfileEnabled && supportsDependencies) + dependenciesPtr = &depfileDependencies; + + auto retVal = compileToSPIRV_impl(code, options, dependenciesPtr); + if (retVal) + { + auto backingBuffer = retVal->getContent(); + const_cast(backingBuffer)->setContentHash(backingBuffer->computeContentHash()); + } + + if (retVal && depfileEnabled && supportsDependencies) + { + const auto* deps = options.writeCache ? &entry.dependencies : &depfileDependencies; + if (!writeDepfileFromDependencies(*deps)) + return nullptr; + } + + if (options.writeCache) + { + if (entry.setContent(retVal->getContent())) + options.writeCache->insert(std::move(entry)); + } + + return retVal; } std::string IShaderCompiler::preprocessShader( @@ -72,7 +321,6 @@ std::string IShaderCompiler::preprocessShader( return preprocessShader(std::move(code), stage, preprocessOptions, dependencies); } - auto IShaderCompiler::IIncludeGenerator::getInclude(const std::string& includeName) const -> IIncludeLoader::found_t { core::vector> builtinNames = getBuiltinNamesToFunctionMapping(); @@ -97,7 +345,7 @@ core::vector IShaderCompiler::IIncludeGenerator::parseArgumentsFrom std::stringstream ss{ _path }; std::string arg; while (std::getline(ss, arg, '/')) - args.push_back(std::move(arg)); + args.emplace_back(std::move(arg)); return args; } @@ -178,7 +426,7 @@ void IShaderCompiler::CIncludeFinder::addSearchPath(const std::string& searchPat { if (!loader) return; - m_loaders.push_back(LoaderSearchPath{ loader, searchPath }); + m_loaders.emplace_back(LoaderSearchPath{ loader, searchPath }); } void IShaderCompiler::CIncludeFinder::addGenerator(const core::smart_refctd_ptr& generatorToAdd) @@ -301,7 +549,7 @@ core::smart_refctd_ptr IShaderCompiler::CCache::serialize() const size_t i = 0u; for (auto& entry : m_container) { // Add the entry as a json array - entries.push_back(entry); + entries.emplace_back(entry); // We keep a copy of the offsets and the sizes of each shader. This is so that later on, when we add the shaders to the buffer after json creation // (where the params array has been moved) we don't have to read the json to get the offsets again diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 736148fb21..21dec40cdf 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -145,10 +145,13 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/indirect_commands.hlsl") # emulated LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/emulated/float64_t.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/emulated/float64_t_impl.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/emulated/int64_common_member_inc.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/emulated/int64_t.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/emulated/vector_t.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/emulated/matrix_t.hlsl") # portable LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/portable/float64_t.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/portable/int64_t.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/portable/vector_t.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/portable/matrix_t.hlsl") # ieee754 @@ -158,7 +161,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ieee754/impl.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/array_accessors.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/vector_utils/vector_traits.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/matrix_utils/matrix_traits.hlsl") - +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/matrix_utils/matrix_runtime_traits.hlsl") #spirv intrinsics LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/spirv_intrinsics/core.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/spirv_intrinsics/fragment_shader_pixel_interlock.hlsl") @@ -177,6 +180,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/cpp_compat/basic.h") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/cpp_compat/intrinsics.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/cpp_compat/matrix.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/cpp_compat/promote.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/cpp_compat/truncate.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/cpp_compat/vector.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/cpp_compat/impl/intrinsics_impl.hlsl") #glsl compat @@ -219,19 +223,26 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/format.hlsl") #linear algebra LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/linalg/fast_affine.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/linalg/transform.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/linalg/basic.hlsl") # TODO: rename `equations` to `polynomials` probably LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/functions.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/geometry.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/intutil.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/polar.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/angle_adding.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/octahedral.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/quaternions.hlsl") +# ies +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ies/profile.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ies/sampler.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ies/texture.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/quadratic.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/cubic.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/quartic.hlsl") #extra math LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/quadrature/gauss_legendre/gauss_legendre.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/quadrature/gauss_legendre/impl.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/thin_lens_projection.hlsl") #acceleration structures LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/acceleration_structures.hlsl") #colorspace @@ -249,13 +260,16 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/circle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/ellipse.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/line.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/beziers.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/triangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/spherical_triangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/spherical_rectangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/aabb.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/obb.hlsl") #sampling LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/basic.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/linear.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/bilinear.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/quantized_sequence.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/concentric_mapping.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/box_muller_transform.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/spherical_triangle.hlsl") @@ -365,5 +379,11 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/rwmc/Resolve.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/rwmc/CascadeAccumulator.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/rwmc/SplattingParameters.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/rwmc/ResolveParameters.hlsl") +#morton codes +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/morton.hlsl") +#testing +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/testing/relative_approx_compare.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/testing/orientation_compare.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/testing/vector_length_compare.hlsl") -ADD_CUSTOM_BUILTIN_RESOURCES(nblBuiltinResourceData NBL_RESOURCES_TO_EMBED "${NBL_ROOT_PATH}/include" "nbl/builtin" "nbl::builtin" "${NBL_ROOT_PATH_BINARY}/include" "${NBL_ROOT_PATH_BINARY}/src" "STATIC" "INTERNAL") \ No newline at end of file +ADD_CUSTOM_BUILTIN_RESOURCES(nblBuiltinResourceData NBL_RESOURCES_TO_EMBED "${NBL_ROOT_PATH}/include" "nbl/builtin" "nbl::builtin" "${NBL_ROOT_PATH_BINARY}/include" "${NBL_ROOT_PATH_BINARY}/src" "STATIC" "INTERNAL") diff --git a/src/nbl/builtin/utils.cmake b/src/nbl/builtin/utils.cmake index d791cd3aa4..6465c2ac6d 100644 --- a/src/nbl/builtin/utils.cmake +++ b/src/nbl/builtin/utils.cmake @@ -82,10 +82,18 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH string(MAKE_C_IDENTIFIER ${_GUARD_SUFFIX_} _GUARD_SUFFIX_) set(_OUTPUT_INCLUDE_SEARCH_DIRECTORY_ "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}") - set(_OUTPUT_HEADER_DIRECTORY_ "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}/${_NAMESPACE_PREFIX_}") + set(_OUTPUT_HEADER_DIRECTORY_BASE "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}/${_NAMESPACE_PREFIX_}") + set(_OUTPUT_SOURCE_DIRECTORY_BASE "${_OUTPUT_SOURCE_DIRECTORY_}") + set(_OUTPUT_INCLUDE_SEARCH_DIRECTORY_CONFIG "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_}/$") + set(_OUTPUT_HEADER_DIRECTORY_ "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_CONFIG}/${_NAMESPACE_PREFIX_}") + set(_OUTPUT_SOURCE_DIRECTORY_ "${_OUTPUT_SOURCE_DIRECTORY_BASE}/$") + set(_OUTPUT_INCLUDE_DIRECTORIES_ + "${_OUTPUT_HEADER_DIRECTORY_}" + "${_OUTPUT_INCLUDE_SEARCH_DIRECTORY_CONFIG}" + ) - file(MAKE_DIRECTORY "${_OUTPUT_HEADER_DIRECTORY_}") - file(MAKE_DIRECTORY "${_OUTPUT_SOURCE_DIRECTORY_}") + file(MAKE_DIRECTORY "${_OUTPUT_HEADER_DIRECTORY_BASE}") + file(MAKE_DIRECTORY "${_OUTPUT_SOURCE_DIRECTORY_BASE}") set(_ITR_ 0) foreach(X IN LISTS _LBR_${_BUNDLE_NAME_}_) # iterate over builtin resources bundle list given bundle name @@ -119,10 +127,10 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH list(APPEND NBL_DEPENDENCY_FILES "${NBL_BUILTIN_HEADER_GEN_PY}") list(APPEND NBL_DEPENDENCY_FILES "${NBL_BUILTIN_DATA_GEN_PY}") - set(NBL_RESOURCES_LIST_FILE "${_OUTPUT_SOURCE_DIRECTORY_}/resources.txt") + set(NBL_RESOURCES_LIST_FILE "${_OUTPUT_SOURCE_DIRECTORY_BASE}/resources-$.txt") string(REPLACE ";" "\n" RESOURCES_ARGS "${_LBR_${_BUNDLE_NAME_}_}") - file(WRITE "${NBL_RESOURCES_LIST_FILE}" "${RESOURCES_ARGS}") + file(GENERATE OUTPUT "${NBL_RESOURCES_LIST_FILE}" CONTENT "${RESOURCES_ARGS}") set(NBL_BUILTIN_RESOURCES_H "${_OUTPUT_HEADER_DIRECTORY_}/${NBL_BS_HEADER_FILENAME}") set(NBL_BUILTIN_RESOURCE_DATA_CPP "${_OUTPUT_SOURCE_DIRECTORY_}/${NBL_BS_DATA_SOURCE_FILENAME}") @@ -139,6 +147,7 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH ) add_custom_command(OUTPUT "${NBL_BUILTIN_RESOURCES_H}" "${NBL_BUILTIN_RESOURCE_DATA_CPP}" "${NBL_BUILTIN_DATA_ARCHIVE_H}" "${NBL_BUILTIN_DATA_ARCHIVE_CPP}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${_OUTPUT_HEADER_DIRECTORY_}" "${_OUTPUT_SOURCE_DIRECTORY_}" COMMAND "${_Python3_EXECUTABLE}" "${NBL_BUILTIN_HEADER_GEN_PY}" ${NBL_BUILTIN_RESOURCES_COMMON_ARGS} --outputBuiltinPath "${NBL_BUILTIN_RESOURCES_H}" --outputArchivePath "${NBL_BUILTIN_DATA_ARCHIVE_H}" --archiveBundlePath "${_BUNDLE_ARCHIVE_ABSOLUTE_PATH_}" --guardSuffix "${_GUARD_SUFFIX_}" --isSharedLibrary "${_SHARED_}" COMMAND "${_Python3_EXECUTABLE}" "${NBL_BUILTIN_DATA_GEN_PY}" ${NBL_BUILTIN_RESOURCES_COMMON_ARGS} --outputBuiltinPath "${NBL_BUILTIN_RESOURCE_DATA_CPP}" --outputArchivePath "${NBL_BUILTIN_DATA_ARCHIVE_CPP}" --bundleAbsoluteEntryPath "${_BUNDLE_SEARCH_DIRECTORY_}/${_BUNDLE_ARCHIVE_ABSOLUTE_PATH_}" --correspondingHeaderFile "${NBL_BS_HEADER_FILENAME}" --xxHash256Exe "$<${_NBL_BR_RUNTIME_HASH_}:$>" COMMENT "Generating \"${_TARGET_NAME_}\"'s sources & headers" @@ -204,8 +213,8 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH endif() target_include_directories(${_TARGET_NAME_} PUBLIC + ${_OUTPUT_INCLUDE_DIRECTORIES_} "${_NABLA_INCLUDE_DIRECTORIES_}" - "${_OUTPUT_HEADER_DIRECTORY_}" ) set_target_properties(${_TARGET_NAME_} PROPERTIES CXX_STANDARD 20) @@ -242,9 +251,9 @@ function(ADD_CUSTOM_BUILTIN_RESOURCES _TARGET_NAME_ _BUNDLE_NAME_ _BUNDLE_SEARCH _ADD_PROPERTY_(BUILTIN_RESOURCES_HEADER_DIRECTORY _OUTPUT_HEADER_DIRECTORY_) _ADD_PROPERTY_(BUILTIN_RESOURCES_SOURCE_DIRECTORY _OUTPUT_SOURCE_DIRECTORY_) _ADD_PROPERTY_(BUILTIN_RESOURCES_HEADERS NBL_BUILTIN_RESOURCES_HEADERS) - _ADD_PROPERTY_(BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY _OUTPUT_INCLUDE_SEARCH_DIRECTORY_) + _ADD_PROPERTY_(BUILTIN_RESOURCES_INCLUDE_SEARCH_DIRECTORY _OUTPUT_INCLUDE_SEARCH_DIRECTORY_CONFIG) if(MSVC AND NBL_SANITIZE_ADDRESS) set_property(TARGET ${_TARGET_NAME_} PROPERTY COMPILE_OPTIONS /fsanitize=address) endif() -endfunction() \ No newline at end of file +endfunction() diff --git a/src/nbl/ext/CMakeLists.txt b/src/nbl/ext/CMakeLists.txt index e0bcd223f6..b4c6cf2b64 100644 --- a/src/nbl/ext/CMakeLists.txt +++ b/src/nbl/ext/CMakeLists.txt @@ -54,6 +54,28 @@ if(NBL_BUILD_TEXT_RENDERING) add_subdirectory(TextRendering) endif() +if(NBL_BUILD_DEBUG_DRAW) + add_subdirectory(DebugDraw) + set(NBL_EXT_DEBUG_DRAW_INCLUDE_DIRS + ${NBL_EXT_DEBUG_DRAW_INCLUDE_DIRS} + PARENT_SCOPE + ) + set(NBL_EXT_DEBUG_DRAW_LIB + ${NBL_EXT_DEBUG_DRAW_LIB} + PARENT_SCOPE + ) +endif() + +add_subdirectory(FullScreenTriangle) +set(NBL_EXT_FULL_SCREEN_TRIANGLE_INCLUDE_DIRS + ${NBL_EXT_FULL_SCREEN_TRIANGLE_INCLUDE_DIRS} + PARENT_SCOPE +) +set(NBL_EXT_FULL_SCREEN_TRIANGLE_LIB + ${NBL_EXT_FULL_SCREEN_TRIANGLE_LIB} + PARENT_SCOPE +) + propagate_changed_variables_to_parent_scope() -NBL_ADJUST_FOLDERS(ext) \ No newline at end of file +NBL_ADJUST_FOLDERS(ext) diff --git a/src/nbl/ext/DebugDraw/CDrawAABB.cpp b/src/nbl/ext/DebugDraw/CDrawAABB.cpp new file mode 100644 index 0000000000..ca82da688a --- /dev/null +++ b/src/nbl/ext/DebugDraw/CDrawAABB.cpp @@ -0,0 +1,370 @@ +// 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/ext/DebugDraw/CDrawAABB.h" + +#ifdef NBL_EMBED_BUILTIN_RESOURCES +#include "nbl/ext/debug_draw/builtin/build/CArchive.h" +#endif + +#include "nbl/ext/DebugDraw/builtin/build/spirv/keys.hpp" + +using namespace nbl; +using namespace core; +using namespace video; +using namespace system; +using namespace asset; +using namespace hlsl; + +namespace nbl::ext::debug_draw +{ + +core::smart_refctd_ptr DrawAABB::create(SCreationParameters&& params) +{ + auto* const logger = params.utilities->getLogger(); + + if (!params.validate()) + { + logger->log("Failed creation parameters validation!", ILogger::ELL_ERROR); + return nullptr; + } + + ConstructorParams constructorParams; + + if (params.drawMode & ADM_DRAW_SINGLE) + { + auto pipelineLayout = params.singlePipelineLayout; + if (!pipelineLayout) + pipelineLayout = createDefaultPipelineLayout(params.utilities->getLogicalDevice(), ADM_DRAW_SINGLE); + constructorParams.singlePipeline = createPipeline(params, pipelineLayout.get(), ADM_DRAW_SINGLE); + if (!constructorParams.singlePipeline) + { + logger->log("Failed to create pipeline!", ILogger::ELL_ERROR); + return nullptr; + } + } + + if (params.drawMode & ADM_DRAW_BATCH) + { + auto pipelineLayout = params.batchPipelineLayout; + if (!pipelineLayout) + pipelineLayout = createDefaultPipelineLayout(params.utilities->getLogicalDevice(), ADM_DRAW_BATCH); + constructorParams.batchPipeline = createPipeline(params, pipelineLayout.get(), ADM_DRAW_BATCH); + if (!constructorParams.batchPipeline) + { + logger->log("Failed to create pipeline!", ILogger::ELL_ERROR); + return nullptr; + } + } + + if (!createStreamingBuffer(params)) + { + logger->log("Failed to create streaming buffer!", ILogger::ELL_ERROR); + return nullptr; + } + + constructorParams.indicesBuffer = createIndicesBuffer(params); + if (!constructorParams.indicesBuffer) + { + logger->log("Failed to create indices buffer!", ILogger::ELL_ERROR); + return nullptr; + } + + constructorParams.creationParams = std::move(params); + return core::smart_refctd_ptr(new DrawAABB(std::move(constructorParams))); +} + +// extension data mount alias +constexpr std::string_view NBL_EXT_MOUNT_ENTRY = "nbl/ext/DebugDraw"; + +const smart_refctd_ptr DrawAABB::mount(smart_refctd_ptr logger, ISystem* system, video::ILogicalDevice* device, const std::string_view archiveAlias) +{ + assert(system); + + if (!system) + return nullptr; + + // the key is deterministic, we are validating presence of required .spv + const auto composed = path(archiveAlias.data()) / nbl::ext::debug_draw::builtin::build::get_spirv_key<"draw_aabb">(device); + if (system->exists(composed, {})) + return nullptr; + + // extension should mount everything for you, regardless if content goes from virtual filesystem + // or disk directly - and you should never rely on application framework to expose extension data + #ifdef NBL_EMBED_BUILTIN_RESOURCES + auto archive = make_smart_refctd_ptr(smart_refctd_ptr(logger)); + #else + auto archive = make_smart_refctd_ptr(std::string_view(NBL_DEBUG_DRAW_HLSL_MOUNT_POINT), smart_refctd_ptr(logger), system); + #endif + + system->mount(smart_refctd_ptr(archive), archiveAlias.data()); + return smart_refctd_ptr(archive); +} + +smart_refctd_ptr DrawAABB::createPipeline(SCreationParameters& params, const IGPUPipelineLayout* pipelineLayout, DrawMode mode) +{ + system::logger_opt_ptr logger = params.utilities->getLogger(); + auto system = smart_refctd_ptr(params.assetManager->getSystem()); + auto* device = params.utilities->getLogicalDevice(); + mount(smart_refctd_ptr(params.utilities->getLogger()), system.get(), params.utilities->getLogicalDevice(), NBL_EXT_MOUNT_ENTRY); + + auto getShader = [&](const core::string& key)->smart_refctd_ptr { + IAssetLoader::SAssetLoadParams lp = {}; + lp.logger = params.utilities->getLogger(); + lp.workingDirectory = NBL_EXT_MOUNT_ENTRY; + auto bundle = params.assetManager->getAsset(key.c_str(), lp); + + const auto contents = bundle.getContents(); + + if (contents.empty()) + { + logger.log("Failed to load shader %s from disk", ILogger::ELL_ERROR, key.c_str()); + return nullptr; + } + + if (bundle.getAssetType() != IAsset::ET_SHADER) + { + logger.log("Loaded asset has wrong type!", ILogger::ELL_ERROR); + return nullptr; + } + + return IAsset::castDown(contents[0]); + }; + + const auto key = nbl::ext::debug_draw::builtin::build::get_spirv_key<"draw_aabb">(device); + smart_refctd_ptr unifiedShader = getShader(key); + if (!unifiedShader) + { + params.utilities->getLogger()->log("Could not compile shaders!", ILogger::ELL_ERROR); + return nullptr; + } + + video::IGPUGraphicsPipeline::SCreationParams pipelineParams[1] = {}; + pipelineParams[0].layout = pipelineLayout; + pipelineParams[0].vertexShader = { .shader = unifiedShader.get(), .entryPoint = (mode & ADM_DRAW_SINGLE) ? "aabb_vertex_single" : "aabb_vertex_instances" }; + pipelineParams[0].fragmentShader = { .shader = unifiedShader.get(), .entryPoint = "aabb_fragment" }; + pipelineParams[0].cached = { + .primitiveAssembly = { + .primitiveType = asset::E_PRIMITIVE_TOPOLOGY::EPT_LINE_LIST, + } + }; + pipelineParams[0].renderpass = params.renderpass.get(); + + smart_refctd_ptr pipeline; + params.utilities->getLogicalDevice()->createGraphicsPipelines(nullptr, pipelineParams, &pipeline); + if (!pipeline) + { + params.utilities->getLogger()->log("Could not create streaming pipeline!", ILogger::ELL_ERROR); + return nullptr; + } + + return pipeline; +} + +bool DrawAABB::createStreamingBuffer(SCreationParameters& params) +{ + const uint32_t minStreamingBufferAllocationSize = 128u, maxStreamingBufferAllocationAlignment = 4096u, mdiBufferDefaultSize = /* 2MB */ 1024u * 1024u * 2u; + + auto getRequiredAccessFlags = [&](const bitflag& properties) + { + bitflag flags(IDeviceMemoryAllocation::EMCAF_NO_MAPPING_ACCESS); + + if (properties.hasFlags(IDeviceMemoryAllocation::EMPF_HOST_WRITABLE_BIT)) + flags |= IDeviceMemoryAllocation::EMCAF_WRITE; + + return flags; + }; + + if (!params.streamingBuffer) + { + IGPUBuffer::SCreationParams mdiCreationParams = {}; + mdiCreationParams.usage = SCachedCreationParameters::RequiredUsageFlags; + mdiCreationParams.size = mdiBufferDefaultSize; + + auto buffer = params.utilities->getLogicalDevice()->createBuffer(std::move(mdiCreationParams)); + buffer->setObjectDebugName("AABB Streaming Buffer"); + + auto memoryReqs = buffer->getMemoryReqs(); + memoryReqs.memoryTypeBits &= params.utilities->getLogicalDevice()->getPhysicalDevice()->getUpStreamingMemoryTypeBits(); + + auto allocation = params.utilities->getLogicalDevice()->allocate(memoryReqs, buffer.get(), SCachedCreationParameters::RequiredAllocateFlags); + { + const bool allocated = allocation.isValid(); + assert(allocated); + } + auto memory = allocation.memory; + + if (!memory->map({ 0ull, memoryReqs.size }, getRequiredAccessFlags(memory->getMemoryPropertyFlags()))) + params.utilities->getLogger()->log("Could not map device memory!", ILogger::ELL_ERROR); + + params.streamingBuffer = make_smart_refctd_ptr(SBufferRange{0ull, mdiCreationParams.size, std::move(buffer)}, maxStreamingBufferAllocationAlignment, minStreamingBufferAllocationSize); + } + + auto buffer = params.streamingBuffer->getBuffer(); + auto binding = buffer->getBoundMemory(); + + const auto validation = std::to_array + ({ + std::make_pair(buffer->getCreationParams().usage.hasFlags(SCachedCreationParameters::RequiredUsageFlags), "Streaming buffer must be created with IBuffer::EUF_STORAGE_BUFFER_BIT | IBuffer::EUF_SHADER_DEVICE_ADDRESS_BIT enabled!"), + std::make_pair(binding.memory->getAllocateFlags().hasFlags(SCachedCreationParameters::RequiredAllocateFlags), "Streaming buffer's memory must be allocated with IDeviceMemoryAllocation::EMAF_DEVICE_ADDRESS_BIT enabled!"), + std::make_pair(binding.memory->isCurrentlyMapped(), "Streaming buffer's memory must be mapped!"), // streaming buffer contructor already validates it, but cannot assume user won't unmap its own buffer for some reason (sorry if you have just hit it) + std::make_pair(binding.memory->getCurrentMappingAccess().hasFlags(getRequiredAccessFlags(binding.memory->getMemoryPropertyFlags())), "Streaming buffer's memory current mapping access flags don't meet requirements!") + }); + + for (const auto& [ok, error] : validation) + if (!ok) + { + params.utilities->getLogger()->log(error, ILogger::ELL_ERROR); + return false; + } + + return true; +} + +smart_refctd_ptr DrawAABB::createIndicesBuffer(SCreationParameters& params) +{ + std::array unitAABBIndices; + unitAABBIndices[0] = 0; + unitAABBIndices[1] = 1; + unitAABBIndices[2] = 0; + unitAABBIndices[3] = 2; + + unitAABBIndices[4] = 3; + unitAABBIndices[5] = 1; + unitAABBIndices[6] = 3; + unitAABBIndices[7] = 2; + + unitAABBIndices[8] = 4; + unitAABBIndices[9] = 5; + unitAABBIndices[10] = 4; + unitAABBIndices[11] = 6; + + unitAABBIndices[12] = 7; + unitAABBIndices[13] = 5; + unitAABBIndices[14] = 7; + unitAABBIndices[15] = 6; + + unitAABBIndices[16] = 0; + unitAABBIndices[17] = 4; + unitAABBIndices[18] = 1; + unitAABBIndices[19] = 5; + + unitAABBIndices[20] = 2; + unitAABBIndices[21] = 6; + unitAABBIndices[22] = 3; + unitAABBIndices[23] = 7; + + auto* device = params.utilities->getLogicalDevice(); + smart_refctd_ptr cmdbuf; + { + smart_refctd_ptr cmdpool = device->createCommandPool(params.transfer->getFamilyIndex(), IGPUCommandPool::CREATE_FLAGS::TRANSIENT_BIT); + if (!cmdpool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY, { &cmdbuf, 1 })) + { + params.utilities->getLogger()->log("Failed to create Command Buffer for index buffer!\n"); + return nullptr; + } + } + + IGPUBuffer::SCreationParams bufparams; + bufparams.size = sizeof(uint32_t) * unitAABBIndices.size(); + bufparams.usage = IGPUBuffer::EUF_INDEX_BUFFER_BIT | IGPUBuffer::EUF_TRANSFER_DST_BIT | IGPUBuffer::EUF_INLINE_UPDATE_VIA_CMDBUF; + + smart_refctd_ptr indicesBuffer; + { + indicesBuffer = device->createBuffer(std::move(bufparams)); + if (!indicesBuffer) + { + params.utilities->getLogger()->log("Failed to create index buffer!\n"); + return nullptr; + } + + video::IDeviceMemoryBacked::SDeviceMemoryRequirements reqs = indicesBuffer->getMemoryReqs(); + reqs.memoryTypeBits &= device->getPhysicalDevice()->getDeviceLocalMemoryTypeBits(); + + auto bufMem = device->allocate(reqs, indicesBuffer.get()); + if (!bufMem.isValid()) + { + params.utilities->getLogger()->log("Failed to allocate device memory compatible with index buffer!\n"); + return nullptr; + } + } + + { + cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT); + cmdbuf->beginDebugMarker("Fill indices buffer begin"); + + SBufferRange bufRange = { .offset = 0, .size = indicesBuffer->getSize(), .buffer = indicesBuffer }; + cmdbuf->updateBuffer(bufRange, unitAABBIndices.data()); + + cmdbuf->endDebugMarker(); + cmdbuf->end(); + } + + smart_refctd_ptr idxBufProgress; + constexpr auto FinishedValue = 25; + { + constexpr auto StartedValue = 0; + idxBufProgress = device->createSemaphore(StartedValue); + + IQueue::SSubmitInfo submitInfos[1] = {}; + const IQueue::SSubmitInfo::SCommandBufferInfo cmdbufs[] = { {.cmdbuf = cmdbuf.get()} }; + submitInfos[0].commandBuffers = cmdbufs; + const IQueue::SSubmitInfo::SSemaphoreInfo signals[] = { {.semaphore = idxBufProgress.get(),.value = FinishedValue,.stageMask = asset::PIPELINE_STAGE_FLAGS::ALL_TRANSFER_BITS} }; + submitInfos[0].signalSemaphores = signals; + + params.transfer->submit(submitInfos); + } + + const ISemaphore::SWaitInfo waitInfos[] = { { + .semaphore = idxBufProgress.get(), + .value = FinishedValue + } }; + device->blockForSemaphores(waitInfos); + + return indicesBuffer; +} + +core::smart_refctd_ptr DrawAABB::createPipelineLayoutFromPCRange(video::ILogicalDevice* device, const asset::SPushConstantRange& pcRange) +{ + return device->createPipelineLayout({ &pcRange , 1 }, nullptr, nullptr, nullptr, nullptr); +} + +core::smart_refctd_ptr DrawAABB::createDefaultPipelineLayout(video::ILogicalDevice* device, DrawMode mode) +{ + const uint32_t offset = (mode & ADM_DRAW_BATCH) ? offsetof(ext::debug_draw::PushConstants, ipc) : offsetof(ext::debug_draw::PushConstants, spc); + const uint32_t pcSize = (mode & ADM_DRAW_BATCH) ? sizeof(SInstancedPC) : sizeof(SSinglePC); + SPushConstantRange pcRange = { + .stageFlags = IShader::E_SHADER_STAGE::ESS_VERTEX, + .offset = offset, + .size = pcSize + }; + return createPipelineLayoutFromPCRange(device, pcRange); +} + +bool DrawAABB::renderSingle(const DrawParameters& params, const hlsl::shapes::AABB<3, float>& aabb, const hlsl::float32_t4& color) +{ + if (!(m_cachedCreationParams.drawMode & ADM_DRAW_SINGLE)) + { + m_cachedCreationParams.utilities->getLogger()->log("DrawAABB has not been enabled for draw single!", ILogger::ELL_ERROR); + return false; + } + + auto& commandBuffer = params.commandBuffer; + commandBuffer->bindGraphicsPipeline(m_singlePipeline.get()); + commandBuffer->setLineWidth(params.lineWidth); + asset::SBufferBinding indexBinding = { .offset = 0, .buffer = m_indicesBuffer }; + commandBuffer->bindIndexBuffer(indexBinding, asset::EIT_32BIT); + + SSinglePC pc; + hlsl::float32_t3x4 instanceTransform = getTransformFromAABB(aabb); + pc.instance.transform = math::linalg::promoted_mul(params.cameraMat, instanceTransform); + pc.instance.color = color; + + commandBuffer->pushConstants(m_singlePipeline->getLayout(), ESS_VERTEX, offsetof(ext::debug_draw::PushConstants, spc), sizeof(SSinglePC), &pc); + commandBuffer->drawIndexed(IndicesCount, 1, 0, 0, 0); + + return true; +} + +} diff --git a/src/nbl/ext/DebugDraw/CMakeLists.txt b/src/nbl/ext/DebugDraw/CMakeLists.txt new file mode 100644 index 0000000000..812abbce1b --- /dev/null +++ b/src/nbl/ext/DebugDraw/CMakeLists.txt @@ -0,0 +1,71 @@ +include(${NBL_ROOT_PATH}/cmake/common.cmake) + +set(NBL_EXT_INTERNAL_INCLUDE_DIR "${NBL_ROOT_PATH}/include") + +set(NBL_EXT_DEBUG_DRAW_H + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/nbl/ext/DebugDraw/CDrawAABB.h +) + +set(NBL_EXT_DEBUG_DRAW_SRC + "${CMAKE_CURRENT_SOURCE_DIR}/CDrawAABB.cpp" +) + +nbl_create_ext_library_project( + DEBUG_DRAW + "${NBL_EXT_DEBUG_DRAW_H}" + "${NBL_EXT_DEBUG_DRAW_SRC}" + "${NBL_EXT_DEBUG_DRAW_EXTERNAL_INCLUDE}" + "" + "" +) + +get_filename_component(_ARCHIVE_ABSOLUTE_ENTRY_PATH_ "${NBL_EXT_INTERNAL_INCLUDE_DIR}" ABSOLUTE) + +set(NBL_DEBUG_DRAW_HLSL_MOUNT_POINT "${_ARCHIVE_ABSOLUTE_ENTRY_PATH_}/nbl/ext/DebugDraw/builtin/hlsl") +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + ${NBL_DEBUG_DRAW_HLSL_MOUNT_POINT}/common.hlsl + ${NBL_DEBUG_DRAW_HLSL_MOUNT_POINT}/draw_aabb.unified.hlsl +) +target_sources(${LIB_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "${NBL_DEBUG_DRAW_HLSL_MOUNT_POINT}/draw_aabb.unified.hlsl", + "KEY": "draw_aabb", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${NBL_ROOT_PATH}/include" # a workaround due to debug draw ext common header which is not part of Nabla builtin archive + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${LIB_NAME}SPIRV + LINK_TO ${LIB_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_DEBUG_DRAW_HLSL_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/ext/DebugDraw/builtin/build/spirv/keys.hpp + NAMESPACE nbl::ext::debug_draw::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::ext::debug_draw::builtin::build + TARGET ${LIB_NAME}_builtinsBuild + LINK_TO ${LIB_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) + +add_library(Nabla::ext::DebugDraw ALIAS ${LIB_NAME}) \ No newline at end of file diff --git a/src/nbl/ext/FullScreenTriangle/CFullScreenTriangle.cpp b/src/nbl/ext/FullScreenTriangle/CFullScreenTriangle.cpp new file mode 100644 index 0000000000..a825c580a9 --- /dev/null +++ b/src/nbl/ext/FullScreenTriangle/CFullScreenTriangle.cpp @@ -0,0 +1,128 @@ +// Copyright (C) 2018-2026 - 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/ext/FullScreenTriangle/FullScreenTriangle.h" + +#ifdef NBL_EMBED_BUILTIN_RESOURCES +#include "nbl/ext/FullScreenTriangle/builtin/build/CArchive.h" +#endif + +#include "nbl/ext/FullScreenTriangle/builtin/build/spirv/keys.hpp" + +using namespace nbl; +using namespace core; +using namespace video; +using namespace system; +using namespace asset; + +namespace nbl::ext::FullScreenTriangle +{ + +constexpr std::string_view NBL_EXT_MOUNT_ENTRY = "nbl/ext/FullScreenTriangle"; +constexpr std::string_view VertexEntryPoint = "__nbl__hlsl__ext__FullScreenTriangle__vertex_main"; + +smart_refctd_ptr ProtoPipeline::mount(smart_refctd_ptr logger, ISystem* system, ILogicalDevice* device, const std::string_view archiveAlias) +{ + assert(system); + if (!system) + return nullptr; + + const auto composed = path(archiveAlias.data()) / builtin::build::get_spirv_key<"full_screen_triangle_vertex">(device); + if (system->exists(composed, {})) + return nullptr; + +#ifdef NBL_EMBED_BUILTIN_RESOURCES + auto archive = make_smart_refctd_ptr(smart_refctd_ptr(logger)); +#else + auto archive = make_smart_refctd_ptr(std::string_view(NBL_FULL_SCREEN_TRIANGLE_HLSL_MOUNT_POINT), smart_refctd_ptr(logger), system); +#endif + + system->mount(smart_refctd_ptr(archive), archiveAlias.data()); + return smart_refctd_ptr(archive); +} + +smart_refctd_ptr ProtoPipeline::createDefaultVertexShader(IAssetManager* assMan, ILogicalDevice* device, ILogger* logger) +{ + if (!assMan || !device) + return nullptr; + + auto system = smart_refctd_ptr(assMan->getSystem()); + if (system) + ProtoPipeline::mount(smart_refctd_ptr(logger), system.get(), device, NBL_EXT_MOUNT_ENTRY); + + IAssetLoader::SAssetLoadParams lp = {}; + lp.logger = logger; + lp.workingDirectory = NBL_EXT_MOUNT_ENTRY.data(); + + const auto key = builtin::build::get_spirv_key<"full_screen_triangle_vertex">(device); + auto bundle = assMan->getAsset(key.c_str(), lp); + const auto assets = bundle.getContents(); + if (assets.empty()) + return nullptr; + + auto source = IAsset::castDown(assets[0]); + if (!source) + return nullptr; + + return device->compileShader({.source = source.get(), .stage = hlsl::ESS_VERTEX}); +} + +ProtoPipeline::ProtoPipeline(IAssetManager* assMan, ILogicalDevice* device, ILogger* logger) +{ + m_vxShader = createDefaultVertexShader(assMan, device, logger); +} + +ProtoPipeline::operator bool() const +{ + return m_vxShader.get(); +} + +smart_refctd_ptr ProtoPipeline::createPipeline( + const IGPUPipelineBase::SShaderSpecInfo& fragShader, + IGPUPipelineLayout* layout, + const IGPURenderpass* renderpass, + const uint32_t subpassIx, + SBlendParams blendParams, + asset::SRasterizationParams rasterizationParams, + const hlsl::SurfaceTransform::FLAG_BITS swapchainTransform) +{ + if (!renderpass || !bool(*this) || hlsl::bitCount(swapchainTransform) != 1) + return nullptr; + + auto device = const_cast(renderpass->getOriginDevice()); + + smart_refctd_ptr m_retval; + { + const auto orientationAsUint32 = static_cast(swapchainTransform); + + IGPUPipelineBase::SShaderEntryMap specConstants; + specConstants[0] = std::span{ reinterpret_cast(&orientationAsUint32), sizeof(orientationAsUint32) }; + + IGPUGraphicsPipeline::SCreationParams params[1]; + params[0].layout = layout; + params[0].vertexShader = { .shader = m_vxShader.get(), .entryPoint = VertexEntryPoint.data(), .entries = &specConstants }; + params[0].fragmentShader = fragShader; + params[0].cached = { + .vertexInput = {}, // The Full Screen Triangle doesn't use any HW vertex input state + .primitiveAssembly = {}, + .rasterization = rasterizationParams, + .blend = blendParams, + .subpassIx = subpassIx + }; + params[0].renderpass = renderpass; + + if (!device->createGraphicsPipelines(nullptr, params, &m_retval)) + return nullptr; + } + return m_retval; +} + +bool recordDrawCall(IGPUCommandBuffer* commandBuffer) +{ + constexpr auto VERTEX_COUNT = 3; + constexpr auto INSTANCE_COUNT = 1; + return commandBuffer->draw(VERTEX_COUNT, INSTANCE_COUNT, 0, 0); +} + +} diff --git a/src/nbl/ext/FullScreenTriangle/CMakeLists.txt b/src/nbl/ext/FullScreenTriangle/CMakeLists.txt new file mode 100644 index 0000000000..6d30fd81bc --- /dev/null +++ b/src/nbl/ext/FullScreenTriangle/CMakeLists.txt @@ -0,0 +1,59 @@ +include(common) + +set(HEADERS + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/nbl/ext/FullScreenTriangle/FullScreenTriangle.h +) + +set(SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/CFullScreenTriangle.cpp +) + +nbl_create_ext_library_project(FULL_SCREEN_TRIANGLE + "${HEADERS}" + "${SRCS}" + "" + "" + "" +) + +get_filename_component(DIR "${NBL_ROOT_PATH}/include/nbl/builtin/hlsl/ext/FullScreenTriangle" ABSOLUTE) +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "${DIR}/default.vert.hlsl", + "KEY": "full_screen_triangle_vertex", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -T vs_${SM} + -E __nbl__hlsl__ext__FullScreenTriangle__vertex_main +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${LIB_NAME}SPIRV + LINK_TO ${LIB_NAME} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_FULL_SCREEN_TRIANGLE_HLSL_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/ext/FullScreenTriangle/builtin/build/spirv/keys.hpp + NAMESPACE nbl::ext::FullScreenTriangle::builtin::build + INPUTS ${JSON} + GLOB_DIR ${DIR} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::ext::FullScreenTriangle::builtin::build + TARGET ${LIB_NAME}_builtinsBuild + LINK_TO ${LIB_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) + +add_library(Nabla::ext::FullScreenTriangle ALIAS ${LIB_NAME}) \ No newline at end of file diff --git a/src/nbl/ext/ImGui/CMakeLists.txt b/src/nbl/ext/ImGui/CMakeLists.txt index 2c339b2b00..e46d93b952 100644 --- a/src/nbl/ext/ImGui/CMakeLists.txt +++ b/src/nbl/ext/ImGui/CMakeLists.txt @@ -33,13 +33,14 @@ target_compile_definitions(${LIB_NAME} PRIVATE _ARCHIVE_ABSOLUTE_ENTRY_PATH_="${ target_compile_definitions(${LIB_NAME} PRIVATE _ARCHIVE_ENTRY_KEY_="${_ARCHIVE_ENTRY_KEY_}") if(NBL_EMBED_BUILTIN_RESOURCES) - # (*) -> I wish we could just take NSC, offline-precompile to SPIRV, embed into builtin resource library (as we did!) but then be smart & adjust at runtime OpDecorate of our resources according to wishes - unfortunately no linker yet we have and we are not going to make one ourselves so we compile imgui shaders at runtime set(_BR_TARGET_ extImguibuiltinResourceData) + # TODO: one uber shader and common.hlsl LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "common.hlsl") + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "pc.hlsl") LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "psinput.hlsl") - LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "vertex.hlsl") # (*) -> this we could precompile [no resources for which set/binding Ixs could be adjusted] but I'm not going to mix stuff - LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "fragment.hlsl") # (*) -> but this we could not since we let users to provide external descriptor set layout + ImGUI textures & sampler state set/binding Ixs (for pipeline layout) at runtime + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "vertex.hlsl") + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "fragment.hlsl") ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_ARCHIVE_ABSOLUTE_ENTRY_PATH_}" "${_ARCHIVE_ENTRY_KEY_}" "nbl::ext::imgui::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") LINK_BUILTIN_RESOURCES_TO_TARGET(${LIB_NAME} ${_BR_TARGET_}) diff --git a/src/nbl/ext/ImGui/ImGui.cpp b/src/nbl/ext/ImGui/ImGui.cpp index f477e96cdf..a0de287559 100644 --- a/src/nbl/ext/ImGui/ImGui.cpp +++ b/src/nbl/ext/ImGui/ImGui.cpp @@ -154,6 +154,14 @@ core::smart_refctd_ptr UI::createPipeline(SCreation smart_refctd_ptr vertex, fragment; } shaders; + if (creationParams.spirv.has_value()) + { + // TODO: since prebuild is experminetal currently I don't validate anything + auto& spirv = creationParams.spirv.value(); + shaders.vertex = spirv.vertex; + shaders.fragment = spirv.fragment; + } + else { //! proxy the system, we will touch it gently auto system = smart_refctd_ptr(creationParams.assetManager->getSystem()); @@ -269,18 +277,18 @@ core::smart_refctd_ptr UI::createPipeline(SCreation shaders.vertex = createShader.template operator() < NBL_CORE_UNIQUE_STRING_LITERAL_TYPE("vertex.hlsl"), IShader::E_SHADER_STAGE::ESS_VERTEX > (); shaders.fragment = createShader.template operator() < NBL_CORE_UNIQUE_STRING_LITERAL_TYPE("fragment.hlsl"), IShader::E_SHADER_STAGE::ESS_FRAGMENT > (); + } - if (!shaders.vertex) - { - creationParams.utilities->getLogger()->log("Failed to compile vertex shader!", ILogger::ELL_ERROR); - return nullptr; - } + if (!shaders.vertex) + { + creationParams.utilities->getLogger()->log("Failed to create vertex shader!", ILogger::ELL_ERROR); + return nullptr; + } - if (!shaders.fragment) - { - creationParams.utilities->getLogger()->log("Failed to compile fragment shader!", ILogger::ELL_ERROR); - return nullptr; - } + if (!shaders.fragment) + { + creationParams.utilities->getLogger()->log("Failed to create fragment shader!", ILogger::ELL_ERROR); + return nullptr; } SVertexInputParams vertexInputParams{}; @@ -332,6 +340,7 @@ core::smart_refctd_ptr UI::createPipeline(SCreation rasterizationParams.faceCullingMode = EFCM_NONE; rasterizationParams.depthWriteEnable = false; rasterizationParams.depthBoundsTestEnable = false; + rasterizationParams.depthCompareOp = ECO_ALWAYS; rasterizationParams.viewportCount = creationParams.viewportCount; } @@ -969,26 +978,48 @@ bool UI::createMDIBuffer(SCreationParameters& creationParams) return flags; }; + auto* device = creationParams.utilities->getLogicalDevice(); + const auto* physDev = device->getPhysicalDevice(); + const auto upStreamingBits = physDev->getUpStreamingMemoryTypeBits(); + const auto hostVisibleBits = physDev->getHostVisibleMemoryTypeBits(); + bool usedFallback = false; + if (!creationParams.streamingBuffer) { IGPUBuffer::SCreationParams mdiCreationParams = {}; mdiCreationParams.usage = SCachedCreationParams::RequiredUsageFlags; mdiCreationParams.size = mdiBufferDefaultSize; - auto buffer = creationParams.utilities->getLogicalDevice()->createBuffer(std::move(mdiCreationParams)); + auto buffer = device->createBuffer(std::move(mdiCreationParams)); buffer->setObjectDebugName("MDI Upstream Buffer"); - auto memoryReqs = buffer->getMemoryReqs(); - memoryReqs.memoryTypeBits &= creationParams.utilities->getLogicalDevice()->getPhysicalDevice()->getUpStreamingMemoryTypeBits(); + const auto baseReqs = buffer->getMemoryReqs(); + + auto tryAllocate = [&](uint32_t typeBits)->IDeviceMemoryAllocator::SAllocation + { + auto reqs = baseReqs; + reqs.memoryTypeBits &= typeBits; + if (!reqs.memoryTypeBits) + return {}; + return device->allocate(reqs,buffer.get(),SCachedCreationParams::RequiredAllocateFlags); + }; - auto allocation = creationParams.utilities->getLogicalDevice()->allocate(memoryReqs,buffer.get(),SCachedCreationParams::RequiredAllocateFlags); + auto allocation = tryAllocate(upStreamingBits); + if (!allocation.isValid()) + { + allocation = tryAllocate(hostVisibleBits); + usedFallback = allocation.isValid(); + if (usedFallback) + creationParams.utilities->getLogger()->log("ImGui MDI buffer: up-streaming allocation failed, falling back to host-visible memory.", ILogger::ELL_WARNING); + } + if (!allocation.isValid()) { - const bool allocated = allocation.isValid(); - assert(allocated); + creationParams.utilities->getLogger()->log("ImGui MDI buffer: failed to allocate device memory!", ILogger::ELL_ERROR); + return false; } auto memory = allocation.memory; - if (!memory->map({ 0ull, memoryReqs.size }, getRequiredAccessFlags(memory->getMemoryPropertyFlags()))) + if (!memory->map({ 0ull, baseReqs.size }, getRequiredAccessFlags(memory->getMemoryPropertyFlags()))) creationParams.utilities->getLogger()->log("Could not map device memory!", ILogger::ELL_ERROR); creationParams.streamingBuffer = make_smart_refctd_ptr(SBufferRange{0ull,mdiCreationParams.size,std::move(buffer)},maxStreamingBufferAllocationAlignment,minStreamingBufferAllocationSize); @@ -1000,7 +1031,7 @@ bool UI::createMDIBuffer(SCreationParameters& creationParams) const auto validation = std::to_array ({ std::make_pair(buffer->getCreationParams().usage.hasFlags(SCachedCreationParams::RequiredUsageFlags), "MDI buffer must be created with IBuffer::EUF_INDIRECT_BUFFER_BIT | IBuffer::EUF_INDEX_BUFFER_BIT | IBuffer::EUF_VERTEX_BUFFER_BIT | IBuffer::EUF_SHADER_DEVICE_ADDRESS_BIT enabled!"), - std::make_pair(bool(buffer->getMemoryReqs().memoryTypeBits & creationParams.utilities->getLogicalDevice()->getPhysicalDevice()->getUpStreamingMemoryTypeBits()), "MDI buffer must have up-streaming memory type bits enabled!"), + std::make_pair(bool(buffer->getMemoryReqs().memoryTypeBits & (usedFallback ? hostVisibleBits : upStreamingBits)), "MDI buffer must have suitable host-visible memory type bits enabled!"), std::make_pair(binding.memory->getAllocateFlags().hasFlags(SCachedCreationParams::RequiredAllocateFlags), "MDI buffer's memory must be allocated with IDeviceMemoryAllocation::EMAF_DEVICE_ADDRESS_BIT enabled!"), std::make_pair(binding.memory->isCurrentlyMapped(), "MDI buffer's memory must be mapped!"), // streaming buffer contructor already validates it, but cannot assume user won't unmap its own buffer for some reason (sorry if you have just hit it) std::make_pair(binding.memory->getCurrentMappingAccess().hasFlags(getRequiredAccessFlags(binding.memory->getMemoryPropertyFlags())), "MDI buffer's memory current mapping access flags don't meet requirements!") @@ -1487,4 +1518,4 @@ void UI::setContext(void* imguiContext) { ImGui::SetCurrentContext(reinterpret_cast(imguiContext)); } -} \ No newline at end of file +} diff --git a/src/nbl/ext/MitsubaLoader/CElementBSDF.cpp b/src/nbl/ext/MitsubaLoader/CElementBSDF.cpp index a9620c18a2..4117ca5f3a 100644 --- a/src/nbl/ext/MitsubaLoader/CElementBSDF.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementBSDF.cpp @@ -1,140 +1,198 @@ // 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/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" +#include "nbl/ext/MitsubaLoader/CElementBSDF.h" + +#include "nbl/ext/MitsubaLoader/ElementMacros.h" -#include "nbl/ext/MitsubaLoader/CMitsubaMetadata.h" +#include "nbl/builtin/hlsl/complex.hlsl" +#include "nbl/type_traits.h" // legacy stuff for `is_any_of` #include -namespace nbl + +namespace nbl::ext::MitsubaLoader { -namespace ext +namespace impl { -namespace MitsubaLoader +template +struct has_alpha { + constexpr static bool value = std::is_base_of_v || std::is_base_of_v; +}; +template +struct has_diffuseReflectance +{ + constexpr static bool value = std::is_base_of_v || std::is_base_of_v || + std::is_same_v || std::is_same_v; +}; +template +struct can_have_isotropicNDF +{ + constexpr static bool value = std::is_base_of_v || std::is_same_v; +}; +template +struct has_specularReflectance +{ + constexpr static bool value = std::is_base_of_v || std::is_same_v || + std::is_same_v; +}; +} - -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) +auto CElementBSDF::compAddPropertyMap() -> AddPropertyMap { - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr, ""); + using this_t = CElementBSDF; + AddPropertyMap retval; + +// spectrum setting +#define ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(MEMBER,CONSTRAINT,...) { \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(MEMBER,FLOAT,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + state. ## MEMBER = std::move(_property); \ + success = true; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(MEMBER,RGB,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + state. ## MEMBER = std::move(_property); \ + success = true; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(MEMBER,SRGB,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + state. ## MEMBER = std::move(_property); \ + success = true; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(MEMBER,SPECTRUM,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + state. ## MEMBER = std::move(_property); \ + success = true; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; \ +} - static const core::unordered_map StringToType = - { - {"diffuse", CElementBSDF::Type::DIFFUSE}, - {"roughdiffuse", CElementBSDF::Type::ROUGHDIFFUSE}, - {"dielectric", CElementBSDF::Type::DIELECTRIC}, - {"thindielectric", CElementBSDF::Type::THINDIELECTRIC}, - {"roughdielectric", CElementBSDF::Type::ROUGHDIELECTRIC}, - {"conductor", CElementBSDF::Type::CONDUCTOR}, - {"roughconductor", CElementBSDF::Type::ROUGHCONDUCTOR}, - {"plastic", CElementBSDF::Type::PLASTIC}, - {"roughplastic", CElementBSDF::Type::ROUGHPLASTIC}, - {"coating", CElementBSDF::Type::COATING}, - {"roughcoating", CElementBSDF::Type::ROUGHCOATING}, - {"bumpmap", CElementBSDF::Type::BUMPMAP}, - {"normalmap", CElementBSDF::Type::BUMPMAP}, - {"phong", CElementBSDF::Type::PHONG}, - {"ward", CElementBSDF::Type::WARD}, - {"mixturebsdf", CElementBSDF::Type::MIXTURE_BSDF}, - {"blendbsdf", CElementBSDF::Type::BLEND_BSDF}, - {"mask", CElementBSDF::Type::MASK}, - {"twosided", CElementBSDF::Type::TWO_SIDED}, - {"difftrans", CElementBSDF::Type::DIFFUSE_TRANSMITTER}//, - //{"hk", CElementBSDF::Type::HANRAHAN_KRUEGER}, - //{"irawan", CElementBSDF::Type::IRAWAN_MARSCHNER} - }; + // diff trans + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(transmittance,std::is_same,DiffuseTransmitter); - auto found = StringToType.find(type); - if (found==StringToType.end()) - { - ParserLog::invalidXMLFileStructure("unknown type"); - _NBL_DEBUG_BREAK_IF(false); - return CElementFactory::return_type(nullptr, ""); - } + // diffuse + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(reflectance,derived_from,AllDiffuse); + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(diffuseReflectance,impl::has_diffuseReflectance); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(alpha,FLOAT,impl::has_alpha); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(useFastApprox,BOOLEAN,derived_from,AllDiffuse); + + // specular base + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(distribution,STRING,derived_from,RoughSpecularBase) + using ndf_e = RoughSpecularBase::NormalDistributionFunction; + static const core::unordered_map StringToType = + { + {"beckmann",ndf_e::BECKMANN}, + {"ggx", ndf_e::GGX}, + {"phong", ndf_e::PHONG}, + {"as", ndf_e::ASHIKHMIN_SHIRLEY} + }; + auto found = StringToType.find(_property.getProperty()); + if (found==StringToType.end()) + return; + state.distribution = found->second; + success = true; + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; + // COMMON: alpha + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(alphaU,FLOAT,impl::can_have_isotropicNDF); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(alphaV,FLOAT,impl::can_have_isotropicNDF); + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(specularReflectance,impl::has_specularReflectance); + + // conductor + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(eta,derived_from,AllConductor); + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(k,derived_from,AllConductor); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(extEta,FLOAT,derived_from,AllConductor); + // adding twice cause two property types are allowed + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(extEta,STRING,derived_from,AllConductor) + state.extEta = TransmissiveBase::findIOR(_property.getProperty(),logger); + success = true; + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; + // special + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(material,STRING,derived_from,AllConductor) + state = AllConductor(_property.getProperty(),logger); + success = true; + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; + + // transmissive base + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(intIOR,FLOAT,derived_from,TransmissiveBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(extIOR,FLOAT,derived_from,TransmissiveBase); + // adding twice cause two property types are allowed + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(intIOR,STRING,derived_from,TransmissiveBase) + state.intIOR = TransmissiveBase::findIOR(_property.getProperty(),logger); + success = true; + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(extIOR,STRING,derived_from,TransmissiveBase) + state.extIOR = TransmissiveBase::findIOR(_property.getProperty(),logger); + success = true; + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(specularTransmittance,derived_from,TransmissiveBase); + + // plastic + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(nonlinear,BOOLEAN,derived_from,AllPlastic); + // COMMON: diffuseReflectance + + // coating + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(thickness,FLOAT,derived_from,AllCoating); + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(sigmaA,derived_from,AllCoating); + + // bumpmap + // normalmap + + // mixture + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(weights,STRING,std::is_same,MixtureBSDF) + std::istringstream sstr(_property.svalue); + std::string token; + while (std::getline(sstr,token,',')) + { + if (state.weightCount>=MixtureBSDF::MaxChildCount) + { + logger.log(" MaxChildCount of %d exceeded!",system::ILogger::ELL_ERROR,MetaBSDF::MaxChildCount); + break; + } + state.weights[state.weightCount++] = std::stof(token); + } + success = true; + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; - CElementBSDF* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr, ""); + // blend + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(weight,FLOAT,std::is_same,BlendBSDF); - obj->type = found->second; - // defaults - switch (obj->type) - { - case CElementBSDF::Type::DIFFUSE: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHDIFFUSE: - obj->diffuse = CElementBSDF::AllDiffuse(); - break; - case CElementBSDF::Type::DIELECTRIC: - [[fallthrough]]; - case CElementBSDF::Type::THINDIELECTRIC: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHDIELECTRIC: - obj->dielectric = CElementBSDF::AllDielectric(); - break; - case CElementBSDF::Type::CONDUCTOR: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHCONDUCTOR: - obj->conductor = CElementBSDF::AllConductor(); - break; - case CElementBSDF::Type::PLASTIC: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHPLASTIC: - obj->plastic = CElementBSDF::AllPlastic(); - break; - case CElementBSDF::Type::COATING: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHCOATING: - obj->coating = CElementBSDF::AllCoating(); - break; - case CElementBSDF::Type::BUMPMAP: - obj->bumpmap = CElementBSDF::BumpMap(); - obj->bumpmap.wasNormal = strcmp(type,"bumpmap")!=0; - break; - case CElementBSDF::Type::PHONG: - obj->phong = CElementBSDF::Phong(); - break; - case CElementBSDF::Type::WARD: - obj->ward = CElementBSDF::Ward(); - break; - case CElementBSDF::Type::MIXTURE_BSDF: - obj->mixturebsdf = CElementBSDF::MixtureBSDF(); - break; - case CElementBSDF::Type::BLEND_BSDF: - obj->blendbsdf = CElementBSDF::BlendBSDF(); - break; - case CElementBSDF::Type::MASK: - obj->mask = CElementBSDF::Mask(); - break; - case CElementBSDF::Type::TWO_SIDED: - obj->twosided = CElementBSDF::TwoSided(); - break; - case CElementBSDF::Type::DIFFUSE_TRANSMITTER: - obj->difftrans = CElementBSDF::DiffuseTransmitter(); - break; - //case CElementBSDF::Type::HANRAHAN_KRUEGER: - //hk = CElementBSDF::HanrahanKrueger(); - //break; - //case CElementBSDF::Type::IRAWAN_MARSCHNER: - //irawan = CElementBSDF::IrawanMarschner(); - //break; - default: - break; - } - return CElementFactory::return_type(obj, std::move(name)); + // mask + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(opacity,std::is_same,Mask); + + // twosided + + // phong + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(exponent,FLOAT,std::is_same,Phong); + // COMMON: specularReflectance + // COMMON: diffuseReflectance + + // ward + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(variant,STRING,std::is_same,Ward) + static const core::unordered_map StringToType = + { + {"ward", Ward::Type::WARD}, + {"ward-duer", Ward::Type::WARD_DUER}, + {"balanced", Ward::Type::BALANCED} + }; + auto found = StringToType.find(_property.getProperty()); + if (found==StringToType.end()) + return; + state.variant = found->second; + success = true; + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; + // COMMON: alphaU + // COMMON: alphaV + // COMMON: specularReflectance + // COMMON: diffuseReflectance + + // TODO: set HK and IRAWAN parameters, sigmaS, sigmaT, albedo, filename, repeatU, repeatV + +#undef ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED + + return retval; } -float CElementBSDF::TransmissiveBase::findIOR(const std::string& name) +float CElementBSDF::TransmissiveBase::findIOR(const std::string_view name, system::logger_opt_ptr logger) { - static const core::unordered_map NamedIndicesOfRefraction = + static const core::unordered_map NamedIndicesOfRefraction = { {"vacuum", 1.f}, {"helium", 1.00004f}, @@ -167,13 +225,13 @@ float CElementBSDF::TransmissiveBase::findIOR(const std::string& name) } -CElementBSDF::AllConductor::AllConductor(const std::string& material) : RoughSpecularBase(0.1) +CElementBSDF::AllConductor::AllConductor(const std::string_view material, system::logger_opt_ptr logger) : RoughSpecularBase(0.1) { // TODO fill this out with values from http://www.luxpop.com/HU_v173.cgi?OpCode=73 and https://github.com/mmp/pbrt-v3/blob/master/src/materials/metal.cpp or https://refractiveindex.info/?shelf=main&book=Cu&page=Johnson // we use Rec 709 for the Color primaries of this table, so Red ~= 615nm, Green ~= 535nm, Blue ~= 465nm - static const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> NamedConductors = + static const core::unordered_map,3>,core::CaseInsensitiveHash,core::CaseInsensitiveEquals> NamedConductors = { -#define SPECTRUM_MACRO(R,G,B,X,Y,Z) {SPropertyElementData(SPropertyElementData::Type::RGB,core::vectorSIMDf(R,G,B)),SPropertyElementData(SPropertyElementData::Type::RGB,core::vectorSIMDf(X,Y,Z))} +#define SPECTRUM_MACRO(R,G,B,X,Y,Z) {{R,X},{G,Y},{B,Z}} {"a-C", SPECTRUM_MACRO(1.6855f, 1.065f, 1.727f, 0.0f, 0.009f, 0.0263f)}, // there is no "a-C", but "a-C:H; data from palik" {"Ag", SPECTRUM_MACRO(0.059481f, 0.055090f, 0.046878f, 4.1367f, 3.4574f, 2.8028f)}, {"Al", SPECTRUM_MACRO(1.3404f, 0.95151f, 0.68603f, 7.3509f, 6.4542f, 5.6351f)}, @@ -238,581 +296,124 @@ CElementBSDF::AllConductor::AllConductor(const std::string& material) : RoughSpe }; auto found = NamedConductors.find(material); - if (found == NamedConductors.end()) + if (found==NamedConductors.end()) { _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("IoR Table lookup not implemented for material preset: " + material); + logger.log("Named material %s in failed to be found, defaulting to \"none\"",system::ILogger::ELL_ERROR,material.data()); found = NamedConductors.find("none"); assert(found != NamedConductors.end()); } - eta = found->second.first; - k = found->second.second; - extEta = TransmissiveBase::findIOR("air"); + const auto etaK = found->second; + eta = SPropertyElementData(SPropertyElementData::Type::RGB,float32_t4{etaK.r.real(),etaK.g.real(),etaK.b.real(),0.f}); + k = SPropertyElementData(SPropertyElementData::Type::RGB,float32_t4{etaK.r.real(),etaK.g.real(),etaK.b.real(),0.f}); + extEta = TransmissiveBase::findIOR("air",logger); } -bool CElementBSDF::addProperty(SNamedPropertyElement&& _property) + +bool CElementBSDF::processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) { - bool error = false; - auto dispatch = [&](auto func) -> void - { - switch (type) - { - case CElementBSDF::Type::DIFFUSE: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHDIFFUSE: - func(diffuse); - break; - case CElementBSDF::Type::DIELECTRIC: - [[fallthrough]]; - case CElementBSDF::Type::THINDIELECTRIC: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHDIELECTRIC: - func(dielectric); - break; - case CElementBSDF::Type::CONDUCTOR: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHCONDUCTOR: - func(conductor); - break; - case CElementBSDF::Type::PLASTIC: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHPLASTIC: - func(plastic); - break; - case CElementBSDF::Type::COATING: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHCOATING: - func(coating); - break; - case CElementBSDF::Type::BUMPMAP: - func(bumpmap); - break; - case CElementBSDF::Type::PHONG: - func(phong); - break; - case CElementBSDF::Type::WARD: - func(ward); - break; - case CElementBSDF::Type::MIXTURE_BSDF: - func(mixturebsdf); - break; - case CElementBSDF::Type::BLEND_BSDF: - func(blendbsdf); - break; - case CElementBSDF::Type::MASK: - func(mask); - break; - case CElementBSDF::Type::TWO_SIDED: - func(twosided); - break; - case CElementBSDF::Type::DIFFUSE_TRANSMITTER: - func(difftrans); - break; - //case CElementBSDF::Type::HANRAHAN_KRUEGER: - //func(hk); - //break; - //case CElementBSDF::Type::IRAWAN_MARSCHNER: - //func(irwan); - //break; - default: - error = true; - break; - } - }; + if (!_child) + return true; -#define SET_FLOAT(MEMBER, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ - { \ - switch (_property.type) { \ - case SPropertyElementData::Type::FLOAT: \ - state. ## MEMBER = SPropertyElementData(_property); \ - break; \ - default: \ - error = true; \ - break; \ - } \ - } \ - }); \ - } -#define SET_SPECTRUM(MEMBER, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ - { \ - switch (_property.type) { \ - case SPropertyElementData::Type::FLOAT: \ - case SPropertyElementData::Type::RGB: \ - case SPropertyElementData::Type::SRGB: \ - case SPropertyElementData::Type::SPECTRUM: \ - state. ## MEMBER = SPropertyElementData(_property); \ - break; \ - default: \ - error = true; \ - break; \ - } \ - } \ - }); \ - } -#define SET_PROPERTY_TEMPLATE(MEMBER,PROPERTY_TYPE, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ + using this_t = CElementBSDF; + +#define SET_TEXTURE_CONSTRAINED(MEMBER,CONSTRAINT,... ) {#MEMBER,{.func = [](this_t* _this, IElement* _child, const system::logger_opt_ptr logger)->bool \ + { \ + bool success = false; \ + auto _texture = static_cast(_child); \ + _this->visit([&_texture,logger,&success](auto& state)->void \ { \ - if (_property.type!=PROPERTY_TYPE) { \ - error = true; \ - return; \ - } \ - state. ## MEMBER = _property.getProperty(); \ + if constexpr (CONSTRAINT __VA_OPT__(,) __VA_ARGS__>::value) \ + { +#define SET_TEXTURE_CONSTRAINED_END } \ } \ - }); \ - } - - auto processReflectance = SET_SPECTRUM(reflectance,AllDiffuse); - auto processDistribution = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_base_of::value) - { - static const core::unordered_map StringToType = - { - {"beckmann",RoughSpecularBase::NormalDistributionFunction::BECKMANN}, - {"ggx", RoughSpecularBase::NormalDistributionFunction::GGX}, - {"phong", RoughSpecularBase::NormalDistributionFunction::PHONG}, - {"as", RoughSpecularBase::NormalDistributionFunction::ASHIKHMIN_SHIRLEY} - }; - - auto found = StringToType.end(); - if (_property.type==SPropertyElementData::Type::STRING) - found = StringToType.find(_property.getProperty()); - if (found==StringToType.end()) - { - error = true; - return; - } - state.distribution = found->second; - } - }); - }; -#define TRANSMISSIVE_TYPES AllDielectric,AllPlastic,AllCoating -#define SPECULAR_TYPES TRANSMISSIVE_TYPES,AllConductor - auto processAlpha = SET_FLOAT(alpha, AllDiffuse,SPECULAR_TYPES); - auto processAlphaU = SET_FLOAT(alphaU, SPECULAR_TYPES); - auto processAlphaV = SET_FLOAT(alphaV, SPECULAR_TYPES); - auto processUseFastApprox = SET_PROPERTY_TEMPLATE(useFastApprox,SPropertyElementData::Type::BOOLEAN,AllDiffuse); - auto processIntIOR = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (is_any_of::value) - { - if (_property.type==SPropertyElementData::Type::FLOAT) - state.intIOR = _property.getProperty(); - else if (_property.type==SPropertyElementData::Type::STRING) - state.intIOR = TransmissiveBase::findIOR(_property.getProperty()); - else - error = true; - } - }); + ); \ + return success; \ + } \ +}} +#define SET_TEXTURE_CONSTRAINED_SIMPLE(MEMBER,CONSTRAINT,... ) SET_TEXTURE_CONSTRAINED(MEMBER,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + state. ## MEMBER = _texture; \ + success = true; \ +SET_TEXTURE_CONSTRAINED_END + + // TODO: store this somewhere outside a global + static const ProcessChildCallbackMap TextureCallbacks = + { + SET_TEXTURE_CONSTRAINED_SIMPLE(transmittance,std::is_same,DiffuseTransmitter), + SET_TEXTURE_CONSTRAINED_SIMPLE(reflectance,derived_from,AllDiffuse), + SET_TEXTURE_CONSTRAINED_SIMPLE(diffuseReflectance,impl::has_diffuseReflectance), + SET_TEXTURE_CONSTRAINED_SIMPLE(alpha,impl::has_alpha), + SET_TEXTURE_CONSTRAINED_SIMPLE(alphaU,impl::can_have_isotropicNDF), + SET_TEXTURE_CONSTRAINED_SIMPLE(alphaV,impl::can_have_isotropicNDF), + SET_TEXTURE_CONSTRAINED_SIMPLE(specularReflectance,impl::has_specularReflectance), + SET_TEXTURE_CONSTRAINED_SIMPLE(specularTransmittance,derived_from,TransmissiveBase), + SET_TEXTURE_CONSTRAINED_SIMPLE(sigmaA,derived_from,AllCoating), + SET_TEXTURE_CONSTRAINED(,is_any_of,BumpMap,NormalMap) + state.texture = _texture; + success = true; + SET_TEXTURE_CONSTRAINED_END, + SET_TEXTURE_CONSTRAINED_SIMPLE(weight,std::is_same,BlendBSDF), + SET_TEXTURE_CONSTRAINED_SIMPLE(opacity,std::is_same,Mask), + SET_TEXTURE_CONSTRAINED_SIMPLE(exponent,std::is_same,Phong) }; - auto processExtIOR = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; +#undef SET_TEXTURE_CONSTRAINED +#undef SET_TEXTURE_CONSTRAINED_SIMPLE - if constexpr (is_any_of::value) - { - if (_property.type==SPropertyElementData::Type::FLOAT) - state.extIOR = _property.getProperty(); - else if (_property.type==SPropertyElementData::Type::STRING) - state.extIOR = TransmissiveBase::findIOR(_property.getProperty()); - else - error = true; - } - }); - }; - auto processSpecularReflectance = SET_SPECTRUM(specularReflectance, SPECULAR_TYPES,Phong,Ward); - auto processDiffuseReflectance = SET_SPECTRUM(diffuseReflectance, AllDiffuse,AllPlastic,Phong,Ward); - auto processSpecularTransmittance = SET_SPECTRUM(specularTransmittance, TRANSMISSIVE_TYPES); -#undef SPECULAR_TYPES -#undef TRANSMISSIVE_TYPES - auto processMaterial = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) - { - if (_property.type == SPropertyElementData::Type::STRING) - conductor = AllConductor(_property.getProperty()); - else - error = true; - }/* - else - { - if constexpr (std::is_same::value) - { - } - }*/ - }); - }; - auto processEta = SET_SPECTRUM(eta, AllConductor); - auto processK = SET_SPECTRUM(k, AllConductor); - auto processExtEta = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) - { - if (_property.type==SPropertyElementData::Type::FLOAT) - state.extEta = _property.getProperty(); - else if (_property.type==SPropertyElementData::Type::STRING) - state.extEta = TransmissiveBase::findIOR(_property.getProperty()); - else - error = true; - } - }); - }; - auto processNonlinear = SET_PROPERTY_TEMPLATE(nonlinear, SPropertyElementData::Type::BOOLEAN, AllPlastic); - auto processThickness = SET_PROPERTY_TEMPLATE(thickness, SPropertyElementData::Type::FLOAT, AllCoating); - auto processSigmaA = SET_SPECTRUM(sigmaA, AllCoating); - auto processExponent = SET_FLOAT(exponent, Phong); - auto processVariant = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) + switch (_child->getType()) + { + case IElement::Type::TEXTURE: + { + auto found = TextureCallbacks.find(name); + if (found==TextureCallbacks.end()) + found = TextureCallbacks.find(""); + if (found==TextureCallbacks.end()) { - static const core::unordered_map StringToType = - { - {"ward", Ward::Type::WARD}, - {"ward-duer", Ward::Type::WARD_DUER}, - {"balanced", Ward::Type::BALANCED} - }; - auto found = StringToType.end(); - if (_property.type==SPropertyElementData::Type::STRING) - found = StringToType.find(_property.getProperty()); - if (found==StringToType.end()) - { - error = true; - return; - } - state.variant = found->second; + logger.log("No can have nested inside it with name \"%s\"!",system::ILogger::ELL_ERROR,name.c_str()); + return false; } - }); - }; - auto processWeights = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) + if (!found->second(this,_child,logger)) { - if (_property.type!=SPropertyElementData::Type::STRING) - { - error = true; - return; - } - - std::istringstream sstr(_property.getProperty()); - std::string token; - while (std::getline(sstr, token, ',')) - state.weights[state.weightCount++] = std::stof(token); + logger.log( + "Failed to parse with name \"%s\" nested inside of type %d!", + system::ILogger::ELL_ERROR,name.c_str(),type + ); + return false; } - }); - }; - auto processWeight = SET_SPECTRUM(weight, BlendBSDF); - auto processOpacity = SET_SPECTRUM(opacity, Mask); - auto processTransmittance = SET_SPECTRUM(transmittance, DiffuseTransmitter); - // TODO: set HK and IRAWAN parameters - /* - auto processField = [&]() -> void - { - dispatch([&](auto& state) -> void + return true; + } + case IElement::Type::BSDF: { - using state_type = std::remove_reference::type; - if constexpr (std::is_same::value) - { - if (_property.type != SPropertyElementData::Type::STRING) - { - error = true; - return; - } - auto found = StringToType.find(_property.svalue); - if (found!=StringToType.end()) - state.field = found->second; - else - state.field = FieldExtraction::Type::INVALID; - } - }); - }; - */ -#undef SET_FLOAT -#undef SET_SPECTRUM -#undef SET_PROPERTY_TEMPLATE - const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> SetPropertyMap = - { - {"reflectance", processReflectance}, - {"distribution", processDistribution}, - {"alpha", processAlpha}, - {"alphaU", processAlphaU}, - {"alphaV", processAlphaV}, - {"useFastApprox", processUseFastApprox}, - {"intIOR", processIntIOR}, - {"extIOR", processExtIOR}, - {"specularReflectance", processSpecularReflectance}, - {"diffuseReflectance", processDiffuseReflectance}, - {"specularTransmittance", processSpecularTransmittance}, - {"material", processMaterial}, - {"eta", processEta}, - {"k", processK}, - {"extEta", processExtEta}, - {"nonlinear", processNonlinear}, - {"thickness", processThickness}, - {"sigmaA", processSigmaA}, - {"exponent", processExponent}, - {"variant", processVariant}, - {"weights", processWeights}, - {"weight", processWeight}, - {"opacity", processOpacity}, - {"transmittance", processTransmittance}//, - //{"sigmaS", processSigmaS}, - //{"sigmaT", processSigmaT}, - //{"albedo", processAlbedo}, - //{"filename", processFilename}, - //{"repeatU", processRepeatU}, - //{"repeatV", processRepeatV} - }; - - auto found = SetPropertyMap.find(_property.name); - if (found==SetPropertyMap.end()) - { - ParserLog::invalidXMLFileStructure("No BSDF can have such property set with name: "+_property.name); - return false; - } - - found->second(); - return !error; -} - - -bool CElementBSDF::processChildData(IElement* _child, const std::string& name) -{ - if (!_child) - return true; - - switch (_child->getType()) - { - case IElement::Type::TEXTURE: + size_t maxChildCount = 0; { - auto _texture = static_cast(_child); - - bool error = false; - auto dispatch = [&](auto func) -> void - { - switch (type) + const auto* _this = this; + visit([&maxChildCount,_this](const auto& state)->void { - case CElementBSDF::Type::DIFFUSE: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHDIFFUSE: - func(diffuse); - break; - case CElementBSDF::Type::DIELECTRIC: - [[fallthrough]]; - case CElementBSDF::Type::THINDIELECTRIC: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHDIELECTRIC: - func(dielectric); - break; - case CElementBSDF::Type::CONDUCTOR: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHCONDUCTOR: - func(conductor); - break; - case CElementBSDF::Type::PLASTIC: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHPLASTIC: - func(plastic); - break; - case CElementBSDF::Type::COATING: - [[fallthrough]]; - case CElementBSDF::Type::ROUGHCOATING: - func(coating); - break; - case CElementBSDF::Type::BUMPMAP: - func(bumpmap); - break; - case CElementBSDF::Type::PHONG: - func(phong); - break; - case CElementBSDF::Type::WARD: - func(ward); - break; - case CElementBSDF::Type::MIXTURE_BSDF: - func(mixturebsdf); - break; - case CElementBSDF::Type::BLEND_BSDF: - func(blendbsdf); - break; - case CElementBSDF::Type::MASK: - func(mask); - break; - case CElementBSDF::Type::TWO_SIDED: - func(twosided); - break; - case CElementBSDF::Type::DIFFUSE_TRANSMITTER: - func(difftrans); - break; - //case CElementBSDF::Type::HANRAHAN_KRUEGER: - //func(hk); - //break; - //case CElementBSDF::Type::IRAWAN_MARSCHNER: - //func(irwan); - //break; - default: - error = true; - break; + using state_t = std::remove_reference_t; + if constexpr (std::is_base_of_v) + maxChildCount = state_t::MaxChildCount; } - }; -#define SET_TEXTURE(MEMBER, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ - { \ - state. ## MEMBER.value.type = SPropertyElementData::Type::INVALID; \ - state. ## MEMBER.texture = _texture; \ - } \ - }); \ - } - - auto processReflectance = SET_TEXTURE(reflectance, AllDiffuse); -#define TRANSMISSIVE_TYPES AllDielectric,AllPlastic,AllCoating -#define SPECULAR_TYPES TRANSMISSIVE_TYPES,AllConductor - auto processAlpha = SET_TEXTURE(alpha, AllDiffuse,SPECULAR_TYPES); - auto processAlphaU = SET_TEXTURE(alphaU, SPECULAR_TYPES); - auto processAlphaV = SET_TEXTURE(alphaV, SPECULAR_TYPES); - auto processSpecularReflectance = SET_TEXTURE(specularReflectance, SPECULAR_TYPES,Phong,Ward); - auto processDiffuseReflectance = SET_TEXTURE(diffuseReflectance, AllDiffuse,AllPlastic,Phong,Ward); - auto processSpecularTransmittance = SET_TEXTURE(specularTransmittance, TRANSMISSIVE_TYPES); - auto processSigmaA = SET_TEXTURE(sigmaA, AllCoating); - auto processExponent = SET_TEXTURE(exponent, Phong); - auto processWeight = SET_TEXTURE(weight, BlendBSDF); - auto processOpacity = SET_TEXTURE(opacity, Mask); - auto processTransmittance = SET_TEXTURE(transmittance, DiffuseTransmitter); -#undef TRANSMISSIVE_TYPES -#undef SPECULAR_TYPES -#undef SET_TEXTURE - - const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> SetChildMap = - { - {"reflectance", processReflectance}, - {"alpha", processAlpha}, - {"alphaU", processAlphaU}, - {"alphaV", processAlphaV}, - {"specularReflectance", processSpecularReflectance}, - {"diffuseReflectance", processDiffuseReflectance}, - {"specularTransmittance", processSpecularTransmittance}, - {"sigmaA", processSigmaA}, - {"exponent", processExponent}, - {"weight", processWeight}, - {"opacity", processOpacity}, - {"transmittance", processTransmittance}//, - //{"sigmaS", processSigmaS}, - //{"sigmaT", processSigmaT}, - //{"albedo", processAlbedo} - }; - - switch (type) - { - case Type::BUMPMAP: - bumpmap.texture = _texture; - break; - default: - { - auto found = SetChildMap.find(name); - if (found!=SetChildMap.end()) - found->second(); - else - { - _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("No BSDF can have such property set with name: " + name); - return false; - } - } - break; - } - - if (error) - return false; + ); } - break; - case IElement::Type::BSDF: + if (meta_common.childCount(_child); - switch (type) - { - case Type::COATING: - [[fallthrough]]; - case Type::ROUGHCOATING: - if (coating.childCount < AllCoating::MaxChildCount) - coating.bsdf[coating.childCount++] = _bsdf; - else - return false; - break; - case Type::BUMPMAP: - if (bumpmap.childCount < BumpMap::MaxChildCount) - bumpmap.bsdf[bumpmap.childCount++] = _bsdf; - else - return false; - break; - case Type::MIXTURE_BSDF: - if (mixturebsdf.childCount < MixtureBSDF::MaxChildCount) - mixturebsdf.bsdf[mixturebsdf.childCount++] = _bsdf; - else - return false; - break; - case Type::BLEND_BSDF: - if (blendbsdf.childCount < BlendBSDF::MaxChildCount) - blendbsdf.bsdf[blendbsdf.childCount++] = _bsdf; - else - return false; - break; - case Type::MASK: - if (mask.childCount < Mask::MaxChildCount) - mask.bsdf[mask.childCount++] = _bsdf; - else - return false; - break; - case Type::TWO_SIDED: - if (twosided.childCount < TwoSided::MaxChildCount) - twosided.bsdf[twosided.childCount++] = _bsdf; - else - return false; - break; - default: - return false; - break; - } + meta_common.bsdf[meta_common.childCount++] = _bsdf; + return true; } - break; + logger.log(" cannot have more than %d other s nested inside it!",system::ILogger::ELL_ERROR,type,maxChildCount); + return false; + } default: + logger.log("Unsupported <%s> nested inside only and are allowed!",system::ILogger::ELL_ERROR,_child->getLogName()); return false; - break; } return true; } -bool CElementBSDF::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* metadata) +bool CElementBSDF::onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) { - if (type == Type::INVALID) - { - ParserLog::invalidXMLFileStructure(getLogName() + ": type not specified"); - _NBL_DEBUG_BREAK_IF(true); - return true; - } + NBL_EXT_MITSUBA_LOADER_ELEMENT_INVALID_TYPE_CHECK(true); // TODO: Validation { @@ -821,6 +422,4 @@ bool CElementBSDF::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override return true; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementEmissionProfile.cpp b/src/nbl/ext/MitsubaLoader/CElementEmissionProfile.cpp index 2bc7fc727a..89ca3c00e2 100644 --- a/src/nbl/ext/MitsubaLoader/CElementEmissionProfile.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementEmissionProfile.cpp @@ -1,83 +1,55 @@ -// Copyright (C) 2018-2023 - 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/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" #include "nbl/ext/MitsubaLoader/CElementEmissionProfile.h" +#include "nbl/ext/MitsubaLoader/ParserUtil.h" + +#include "nbl/ext/MitsubaLoader/ElementMacros.h" #include -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) +namespace nbl::ext::MitsubaLoader { - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr, ""); - - CElementEmissionProfile* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr, ""); - return CElementFactory::return_type(obj, std::move(name)); -} - -bool CElementEmissionProfile::addProperty(SNamedPropertyElement&& _property) { - if (_property.name == "filename") { - if (_property.type != SPropertyElementData::Type::STRING) { - return false; +auto CElementEmissionProfile::compAddPropertyMap() -> AddPropertyMap +{ + using this_t = CElementEmissionProfile; + AddPropertyMap retval; + + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("filename",STRING) + { + setLimitedString("filename",_this->filename,_property,logger); + return true; } - filename = _property.getProperty(); - return true; - } - else if (_property.name == "normalization") { - if (_property.type != SPropertyElementData::Type::STRING) - return false; - - const auto normalizeS = std::string(_property.getProperty()); - - if (normalizeS == "UNIT_MAX") - normalization = EN_UNIT_MAX; - else if(normalizeS == "UNIT_AVERAGE_OVER_IMPLIED_DOMAIN") - normalization = EN_UNIT_AVERAGE_OVER_IMPLIED_DOMAIN; - else if(normalizeS == "UNIT_AVERAGE_OVER_FULL_DOMAIN") - normalization = EN_UNIT_AVERAGE_OVER_FULL_DOMAIN; - else - normalization = EN_NONE; - - return true; - } - else if (_property.name == "flatten") - { - if (_property.type != SPropertyElementData::Type::FLOAT) - return false; - - flatten = _property.getProperty(); + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("normalization",STRING) + { + const auto normalizeS = std::string(_property.svalue); + if (normalizeS == "UNIT_MAX") + _this->normalization = EN_UNIT_MAX; + else if (normalizeS == "UNIT_AVERAGE_OVER_IMPLIED_DOMAIN") + _this->normalization = EN_UNIT_AVERAGE_OVER_IMPLIED_DOMAIN; + else if (normalizeS == "UNIT_AVERAGE_OVER_FULL_DOMAIN") + _this->normalization = EN_UNIT_AVERAGE_OVER_FULL_DOMAIN; + else + { + logger.log("'s `normalization` is unrecognized: \"%s\"",system::ILogger::ELL_ERROR,normalizeS.c_str()); + _this->normalization = EN_NONE; + } + return true; + } + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(flatten,FLOAT); - return true; - } - else { - ParserLog::invalidXMLFileStructure("No emission profile can have such property set with name: " + _property.name); - return false; - } + return retval; } -bool CElementEmissionProfile::processChildData(IElement* _child, const std::string& name) +bool CElementEmissionProfile::processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) { if (!_child) return true; return false; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementEmitter.cpp b/src/nbl/ext/MitsubaLoader/CElementEmitter.cpp index 1a0389c684..93b77b4c3a 100644 --- a/src/nbl/ext/MitsubaLoader/CElementEmitter.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementEmitter.cpp @@ -1,287 +1,206 @@ // 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/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" +#include "nbl/ext/MitsubaLoader/CElementEmitter.h" + +#include "nbl/ext/MitsubaLoader/ElementMacros.h" +#include "nbl/type_traits.h" // legacy stuff for `is_any_of` #include -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader +#include "nbl/builtin/hlsl/math/linalg/transform.hlsl" +#include "glm/gtc/matrix_transform.hpp" + + +namespace nbl::ext::MitsubaLoader { -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) +auto CElementEmitter::compAddPropertyMap() -> AddPropertyMap { - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr,""); + using this_t = CElementEmitter; + AddPropertyMap retval; - static const core::unordered_map StringToType = - { - {"point", CElementEmitter::Type::POINT}, - {"area", CElementEmitter::Type::AREA}, - {"spot", CElementEmitter::Type::SPOT}, - {"directional", CElementEmitter::Type::DIRECTIONAL}, - {"collimated", CElementEmitter::Type::COLLIMATED},/* - {"sky", CElementEmitter::Type::SKY}, - {"sun", CElementEmitter::Type::SUN}, - {"sunsky", CElementEmitter::Type::SUNSKY},*/ - {"envmap", CElementEmitter::Type::ENVMAP}, - {"constant", CElementEmitter::Type::CONSTANT} - }; + // funky transform setting + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("position",POINT) + { + if (_this->type!=Type::POINT && _this->type!=Type::COLLIMATED) + return false; + for (auto r=0; r<3; r++) + _this->transform.matrix[r][3] = _property.vvalue[r]; + return true; + } + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("direction",VECTOR) + { + // for point lights direction gets concatenated with IES rotation + if (_this->type!=Type::POINT && _this->type!=Type::DIRECTIONAL && _this->type!=Type::COLLIMATED) + return false; + hlsl::float32_t3 up = {0,0,0}; + { + uint32_t index = 0u; + { + float maxDot = std::abs(_property.vvalue[0]); + for (auto i=1u; i<3u; i++) + { + float thisAbs = std::abs(_property.vvalue[i]); + if (thisAbs < maxDot) + { + maxDot = thisAbs; + index = i; + } + } + } + up[index] = hlsl::sign(_property.vvalue[index]); + } + // TODO: check if correct + const hlsl::float32_t3 target = (-_property.vvalue).xyz; + // TODO: after the rm-core matrix PR we need to get rid of the tranpose (I transpose only because of GLM and HLSL mixup) + const auto lookAtGLM = reinterpret_cast(glm::lookAtRH({},target,up)); + const auto lookAt = hlsl::transpose(lookAtGLM); + // turn lookat into a rotation matrix + const auto rotation = hlsl::inverse(hlsl::float32_t3x3(lookAt)); + //_NBL_DEBUG_BREAK_IF(true); // no idea if matrix is correct, looks okay + for (auto r=0; r<3; r++) + _this->transform.matrix[r].xyz = rotation[r]; + return true; + } + }); + + // base + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(samplingWeight,FLOAT,derived_from,SampledEmitter); + +// spectrum setting +#define ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(MEMBER,CONSTRAINT,...) { \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(MEMBER,FLOAT,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + state. ## MEMBER.x = state. ## MEMBER.y = state. ## MEMBER.z = _property.getProperty(); \ + success = true; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(MEMBER,RGB,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + state. ## MEMBER = _property.getProperty(); \ + success = true; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(MEMBER,SRGB,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + state. ## MEMBER = _property.getProperty(); \ + success = true; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(MEMBER,SPECTRUM,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + state. ## MEMBER = _property.getProperty(); \ + success = true; \ + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END; \ +} - auto found = StringToType.find(type); - if (found==StringToType.end()) - { - ParserLog::invalidXMLFileStructure("unknown type"); - _NBL_DEBUG_BREAK_IF(false); - return CElementFactory::return_type(nullptr, ""); - } + // delta + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(intensity,derived_from,DeltaDistributionEmitter); + // point covered by delta - CElementEmitter* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr, ""); + // non zero solid angle + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(radiance,derived_from,SolidAngleEmitter); + // area covered by solid angle - obj->type = found->second; - // defaults - switch (obj->type) - { - case CElementEmitter::Type::POINT: - obj->point = CElementEmitter::Point(); - break; - case CElementEmitter::Type::AREA: - obj->area = CElementEmitter::Area(); - break; - case CElementEmitter::Type::SPOT: - obj->spot = CElementEmitter::Spot(); - break; - case CElementEmitter::Type::DIRECTIONAL: - obj->directional = CElementEmitter::Directional(); - break; - case CElementEmitter::Type::COLLIMATED: - obj->collimated = CElementEmitter::Collimated(); - break;/* - case CElementEmitter::Type::SKY: - obj->sky = CElementEmitter::Sky(); - break; - case CElementEmitter::Type::SUN: - obj->ply = CElementEmitter::Sun(); - break; - case CElementEmitter::Type::SUNSKY: - obj->serialized = CElementEmitter::SunSky(); - break;*/ - case CElementEmitter::Type::ENVMAP: - obj->envmap = CElementEmitter::EnvMap(); - break; - case CElementEmitter::Type::CONSTANT: - obj->constant = CElementEmitter::Constant(); - break; - default: - break; - } - return CElementFactory::return_type(obj, std::move(name)); -} + // directional + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(irradiance,std::is_same,Directional); -bool CElementEmitter::addProperty(SNamedPropertyElement&& _property) -{ - bool error = false; - auto dispatch = [&](auto func) -> void - { - switch (type) - { - case Type::POINT: - func(point); - break; - case Type::AREA: - func(area); - break; - case Type::SPOT: - func(spot); - break; - case Type::DIRECTIONAL: - func(directional); - break; - case Type::COLLIMATED: - func(collimated); - break;/* - case Type::SKY: - func(sky); - break; - case Type::SUN: - func(sun); - break; - case Type::SUNSKY: - func(sunsky); - break;*/ - case Type::ENVMAP: - func(envmap); - break; - case Type::CONSTANT: - func(constant); - break; - default: - error = true; - break; - } - }; + // collimated + ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED(power,std::is_same,Collimated); -#define SET_PROPERTY_TEMPLATE(MEMBER,PROPERTY_TYPE, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ - { \ - if (_property.type!=PROPERTY_TYPE) { \ - error = true; \ - return; \ - } \ - state. ## MEMBER = _property.getProperty(); \ - } \ - }); \ - } -#define SET_SPECTRUM(MEMBER, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ - { \ - switch (_property.type) { \ - case SPropertyElementData::Type::FLOAT: \ - state. ## MEMBER.x = state. ## MEMBER.y = state. ## MEMBER.z = _property.getProperty(); \ - break; \ - case SPropertyElementData::Type::RGB: \ - state. ## MEMBER = _property.getProperty(); \ - break; \ - case SPropertyElementData::Type::SRGB: \ - state. ## MEMBER = _property.getProperty(); \ - break; \ - case SPropertyElementData::Type::SPECTRUM: \ - state. ## MEMBER = _property.getProperty(); \ - break; \ - default: \ - error = true; \ - break; \ - } \ - } \ - }); \ - } +#undef ADD_VARIANT_SPECTRUM_PROPERTY_CONSTRAINED - auto setSamplingWeight = SET_PROPERTY_TEMPLATE(samplingWeight, SNamedPropertyElement::Type::FLOAT, Point,Area,Spot,Directional,Collimated,/*Sky,Sun,SunSky,*/EnvMap,Constant); - auto setIntensity = SET_SPECTRUM(intensity, Point,Spot); - auto setPosition = [&]() -> void { - if (_property.type!=SNamedPropertyElement::Type::POINT || type!=Type::POINT) + // environment map + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("filename",STRING,std::is_same,EnvMap) { - error = true; - return; + setLimitedString("filename",_this->envmap.filename,_property,logger); return true; } - transform.matrix.setTranslation(_property.vvalue); - }; - auto setRadiance = SET_SPECTRUM(radiance, Area,Constant); - auto setCutoffAngle = SET_PROPERTY_TEMPLATE(cutoffAngle, SNamedPropertyElement::Type::FLOAT, Spot); - auto setBeamWidth = SET_PROPERTY_TEMPLATE(beamWidth, SNamedPropertyElement::Type::FLOAT, Spot); - auto setDirection = [&]() -> void { - if (_property.type != SNamedPropertyElement::Type::VECTOR || type != Type::DIRECTIONAL) - { - error = true; - return; - } - core::vectorSIMDf up(0.f); - float maxDot = _property.vvalue[0]; - uint32_t index = 0u; - for (auto i=1u; i<3u; i++) - if (_property.vvalue[i] < maxDot) - { - maxDot = _property.vvalue[i]; - index = i; - } - up[index] = 1.f; - // hope it works - core::matrix3x4SIMD tmp; - core::matrix3x4SIMD::buildCameraLookAtMatrixRH(core::vectorSIMDf(),-_property.vvalue,up).getInverse(tmp); - transform.matrix = core::matrix4SIMD(tmp); - _NBL_DEBUG_BREAK_IF(true); // no idea if matrix is correct - }; - auto setPower = SET_SPECTRUM(power, Collimated); - auto setFilename = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(scale,FLOAT,std::is_same,EnvMap); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(gamma,FLOAT,std::is_same,EnvMap); - if constexpr (std::is_same::value) - { - envmap.filename = std::move(_property); - } - }); - }; - auto setScale = SET_PROPERTY_TEMPLATE(scale, SNamedPropertyElement::Type::FLOAT, EnvMap); - auto setGamma = SET_PROPERTY_TEMPLATE(gamma, SNamedPropertyElement::Type::FLOAT, EnvMap); - //auto setCache = SET_PROPERTY_TEMPLATE(cache, SNamedPropertyElement::Type::BOOLEAN, EnvMap); -#undef SET_SPECTRUM -#undef SET_PROPERTY_TEMPLATE - const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> SetPropertyMap = - { - {"samplingWeight", setSamplingWeight}, - {"intensity", setIntensity}, - {"position", setPosition}, - {"radiance", setRadiance}, - {"cutoffAngle", setCutoffAngle}, - {"beamWidth", setBeamWidth}, - {"direction", setDirection}, - {"power", setPower},/* - {"turbidity", setTurbidity}, - {"", set}, - {"sunRadiusScale", setSunRadiusScale},*/ - {"filename", setFilename}, - {"scale", setScale}, - {"gamma", setGamma}//, - //{"cache", setCache}, - }; +#undef ADD_SPECTRUM + return retval; +} + +bool CElementEmitter::processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) +{ + if (!_child) + return true; - auto found = SetPropertyMap.find(_property.name); - if (found==SetPropertyMap.end()) + switch (_child->getType()) { - _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("No Emitter can have such property set with name: " + _property.name); - return false; + case IElement::Type::TRANSFORM: + { + auto tform = static_cast(_child); + if (name!="toWorld") + { + logger.log("The nested inside needs to be named \"toWorld\"",system::ILogger::ELL_ERROR); + return false; + } + //toWorldType = IElement::Type::TRANSFORM; + switch (type) + { + case Type::POINT: + [[fallthrough]]; + case Type::DIRECTIONAL: + [[fallthrough]]; + case Type::COLLIMATED: + [[fallthrough]]; + case Type::AREA: + [[fallthrough]]; + /* + case Type::SKY: + [[fallthrough]]; + case Type::SUN: + [[fallthrough]]; + case Type::SUNSKY: + [[fallthrough]];*/ + case Type::ENVMAP: + transform = *tform; + return true; + default: + logger.log(" does not support ",system::ILogger::ELL_ERROR,type); + return false; + } + } + break;/* + case IElement::Type::ANIMATION: + auto anim = static_cast(_child); + if (anim->name!="toWorld") + return false; + toWorlType = IElement::Type::ANIMATION; + animation = anim; + return true; + break;*/ + case IElement::Type::EMISSION_PROFILE: + if (type!=Type::AREA && type!=Type::POINT) + { + logger.log(" does not support nested emission profiles, only Point and Area lights do",system::ILogger::ELL_ERROR,type); + return false; + } + area.emissionProfile = static_cast(_child); + return true; + default: + break; } - - found->second(); - return !error; + logger.log(" does not support nested <%s> elements",system::ILogger::ELL_ERROR,type,_child->getLogName()); + return false; } -bool CElementEmitter::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* metadata) +bool CElementEmitter::onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) { // TODO: some more validation switch (type) { case Type::INVALID: - ParserLog::invalidXMLFileStructure(getLogName() + ": type not specified"); + logger.log("'s type not specified!",system::ILogger::ELL_ERROR); _NBL_DEBUG_BREAK_IF(true); return true; break; - case Type::SPOT: - if (std::isnan(spot.beamWidth)) - spot.beamWidth = spot.cutoffAngle * 0.75f; - default: - break; - } - - switch (type) - { case Type::AREA: break; default: - metadata->m_global.m_emitters.push_back(*this); break; } - return true; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementFactory.cpp b/src/nbl/ext/MitsubaLoader/CElementFactory.cpp deleted file mode 100644 index c7690089cd..0000000000 --- a/src/nbl/ext/MitsubaLoader/CElementFactory.cpp +++ /dev/null @@ -1,80 +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/ext/MitsubaLoader/CElementFactory.h" - -#include "nbl/ext/MitsubaLoader/ParserUtil.h" - -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ - - -CElementFactory::return_type CElementFactory::processAlias(const char** _atts, ParserManager* _util) -{ - const char* id = nullptr; - const char* as = nullptr; - std::string name; - if (IElement::areAttributesInvalid(_atts, 4u)) - return CElementFactory::return_type(nullptr,std::move(name)); - - while (*_atts) - { - if (core::strcmpi(_atts[0], "id")==0) - id = _atts[1]; - else if (core::strcmpi(_atts[0], "as")==0) - as = _atts[1]; - else if (core::strcmpi(_atts[0], "name")==0) - name = _atts[1]; - _atts += 2; - } - - if (!id || !as) - return CElementFactory::return_type(nullptr,std::move(name)); - - auto* original = _util->handles[id]; - _util->handles[as] = original; - return CElementFactory::return_type(original,std::move(name)); -} - -CElementFactory::return_type CElementFactory::processRef(const char** _atts, ParserManager* _util) -{ - const char* id; - std::string name; - if (!IElement::getIDAndName(id,name,_atts)) - { - os::Printer::log("[ERROR] Malformed `` element!", ELL_ERROR); - return CElementFactory::return_type(nullptr, std::move(name)); - } - auto* original = _util->handles[id]; - if (!original) - os::Printer::log(std::string("[ERROR] Used a `` element but referenced element not defined in preceeding XML!", ELL_ERROR); - return CElementFactory::return_type(original, std::move(name)); -} - - -const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> CElementFactory::createElementTable = -{ - {"integrator", {CElementFactory::createElement,true}}, - {"sensor", {CElementFactory::createElement,true}}, - {"film", {CElementFactory::createElement,true}}, - {"rfilter", {CElementFactory::createElement,true}}, - {"sampler", {CElementFactory::createElement,true}}, - {"shape", {CElementFactory::createElement,true}}, - {"transform", {CElementFactory::createElement,true}}, - //{"animation", {CElementFactory::createElement,true}}, - {"bsdf", {CElementFactory::createElement,true}}, - {"texture", {CElementFactory::createElement,true}}, - {"emitter", {CElementFactory::createElement,true}}, - {"emissionprofile", {CElementFactory::createElement,true}}, - {"alias", {CElementFactory::processAlias,true}}, - {"ref", {CElementFactory::processRef,true}} -}; - -} -} -} \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementFilm.cpp b/src/nbl/ext/MitsubaLoader/CElementFilm.cpp index ba22c58fb9..c85aa2082d 100644 --- a/src/nbl/ext/MitsubaLoader/CElementFilm.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementFilm.cpp @@ -1,326 +1,144 @@ // 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/ext/MitsubaLoader/CElementFilm.h" #include "nbl/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" +#include "nbl/ext/MitsubaLoader/ElementMacros.h" #include -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader +namespace nbl::ext::MitsubaLoader { - -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) +auto CElementFilm::compAddPropertyMap() -> AddPropertyMap { - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr, ""); - - static const core::unordered_map StringToType = - { - {"hdrfilm", CElementFilm::Type::HDR_FILM}, - {"tiledhdrfilm",CElementFilm::Type::TILED_HDR}, - {"ldrfilm", CElementFilm::Type::LDR_FILM}, - {"mfilm", CElementFilm::Type::MFILM} - }; - - auto found = StringToType.find(type); - if (found==StringToType.end()) - { - ParserLog::invalidXMLFileStructure("unknown type"); - _NBL_DEBUG_BREAK_IF(false); - return CElementFactory::return_type(nullptr, ""); - } - - CElementFilm* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr, ""); - - obj->type = found->second; - // defaults - switch (obj->type) - { - case CElementFilm::Type::LDR_FILM: - obj->fileFormat = CElementFilm::FileFormat::PNG; - //obj->componentFormat = UINT8; - obj->ldrfilm = CElementFilm::LDR(); - break; - case CElementFilm::Type::MFILM: - obj->width = 1; - obj->height = 1; - obj->fileFormat = CElementFilm::FileFormat::MATLAB; - obj->pixelFormat = CElementFilm::PixelFormat::LUMINANCE; - obj->mfilm = CElementFilm::M(); - break; - default: - break; - } - return CElementFactory::return_type(obj, std::move(name)); -} - - -bool CElementFilm::addProperty(SNamedPropertyElement&& _property) -{ - bool error = type==Type::INVALID; -#define SET_PROPERTY(MEMBER,PROPERTY_TYPE) [&]() -> void { \ - if (_property.type!=PROPERTY_TYPE) { \ - error = true; \ - return; \ - } \ - MEMBER = _property.getProperty(); \ - } - auto setWidth = SET_PROPERTY(width,SNamedPropertyElement::Type::INTEGER); - auto setHeight = SET_PROPERTY(height,SNamedPropertyElement::Type::INTEGER); - auto setCropOffsetX = SET_PROPERTY(cropOffsetX,SNamedPropertyElement::Type::INTEGER); - auto setCropOffsetY = SET_PROPERTY(cropOffsetY,SNamedPropertyElement::Type::INTEGER); - auto setCropWidth = SET_PROPERTY(cropWidth,SNamedPropertyElement::Type::INTEGER); - auto setCropHeight = SET_PROPERTY(cropHeight,SNamedPropertyElement::Type::INTEGER); - auto setFileFormat = [&]() -> void - { - if (_property.type!=SNamedPropertyElement::Type::STRING) - { - error = true; - return; - } - static const core::unordered_map StringToType = - { - {"openexr", OPENEXR}, - {"png", PNG}, - {"rgbe", RGBE}, - {"pfm", PFM}, - {"matlab", MATLAB}, - {"mathematica", MATHEMATICA}, - {"numpy", NUMPY} - }; - auto found = StringToType.find(_property.svalue); - if (found==StringToType.end()) - { - error = true; - return; - } - fileFormat = found->second; - }; - auto setPixelFormat = [&]() -> void - { - if (_property.type!=SNamedPropertyElement::Type::STRING) - { - error = true; - return; - } - static const core::unordered_map StringToType = - { - {"luminance", LUMINANCE}, - {"luminanceAlpha", LUMINANCE_ALPHA}, - {"rgb", RGB}, - {"rgba", RGBA}, - {"xyz", XYZ}, - {"xyza", XYZA}, - {"spectrum", SPECTRUM}, - {"spectrumAlpha", SPECTRUM_ALPHA} - }; - auto found = StringToType.find(_property.svalue); - if (found==StringToType.end()) - { - error = true; - return; + using this_t = CElementFilm; + AddPropertyMap retval; + + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(width,INTEGER); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(height,INTEGER); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(cropOffsetX,INTEGER); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(cropOffsetY,INTEGER); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(cropWidth,INTEGER); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(cropHeight,INTEGER); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("fileFormat",STRING) + { + static const core::unordered_map StringToType = + { + {"openexr", OPENEXR}, + {"png", PNG}, + {"rgbe", RGBE}, + {"pfm", PFM}, + {"matlab", MATLAB}, + {"mathematica", MATHEMATICA}, + {"numpy", NUMPY} + }; + auto found = StringToType.find(_property.svalue); + if (found==StringToType.end()) + return false; + _this->fileFormat = found->second; + return true; } - pixelFormat = found->second; - }; - auto setComponentFormat = [&]() -> void - { - if (_property.type!=SNamedPropertyElement::Type::STRING || type==Type::LDR_FILM || type==Type::MFILM) + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("pixelFormat",STRING) { - error = true; - return; + static const core::unordered_map StringToType = + { + {"luminance", LUMINANCE}, + {"luminanceAlpha", LUMINANCE_ALPHA}, + {"rgb", RGB}, + {"rgba", RGBA}, + {"xyz", XYZ}, + {"xyza", XYZA}, + {"spectrum", SPECTRUM}, + {"spectrumAlpha", SPECTRUM_ALPHA} + }; + auto found = StringToType.find(_property.svalue); + if (found==StringToType.end()) + return false; + _this->pixelFormat = found->second; + return true; } - static const core::unordered_map StringToType = - { - {"float16", FLOAT16}, - {"float32", FLOAT32}, - {"uint32", UINT32} - }; - auto found = StringToType.find(_property.svalue); - if (found==StringToType.end()) + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("setComponentFormat",STRING) { - error = true; - return; + static const core::unordered_map StringToType = + { + {"float16", FLOAT16}, + {"float32", FLOAT32}, + {"uint32", UINT32} + }; + auto found = StringToType.find(_property.svalue); + if (found==StringToType.end()) + return false; + _this->componentFormat = found->second; + return true; } - componentFormat = found->second; - }; - auto setBanner = SET_PROPERTY(banner,SNamedPropertyElement::Type::BOOLEAN); - auto setHighQualityEdges= SET_PROPERTY(highQualityEdges,SNamedPropertyElement::Type::BOOLEAN); + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(banner,BOOLEAN); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(highQualityEdges,BOOLEAN); - - auto dispatch = [&](auto func) -> void - { - switch (type) + + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(attachLog,BOOLEAN,std::is_same,HDR); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("tonemapMethod",STRING,std::is_same,LDR) { - case CElementFilm::Type::HDR_FILM: - func(hdrfilm); - break; - case CElementFilm::Type::LDR_FILM: - func(ldrfilm); - break; - case CElementFilm::Type::MFILM: - func(mfilm); - break; - default: - error = true; - break; + static const core::unordered_map StringToType = + { + {"gamma", LDR::GAMMA}, + {"reinhard",LDR::REINHARD} + }; + auto found = StringToType.find(_property.svalue); + if (found==StringToType.end()) + return false; + _this->ldrfilm.tonemapMethod = found->second; + return true; } - }; -#define SET_PROPERTY_TEMPLATE(MEMBER,PROPERTY_TYPE, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ - { \ - if (_property.type!=PROPERTY_TYPE) { \ - error = true; \ - return; \ - } \ - state. ## MEMBER = _property.getProperty(); \ - } \ - }); \ - } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(gamma,FLOAT,std::is_same,LDR); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(exposure,FLOAT,std::is_same,LDR); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(key,FLOAT,std::is_same,LDR); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(burn,FLOAT,std::is_same,LDR); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(attachLog,INTEGER,std::is_same,HDR); - auto setAttachLog = SET_PROPERTY_TEMPLATE(attachLog, SNamedPropertyElement::Type::BOOLEAN, HDR); - auto setTonemapMethod = [&]() -> void - { - if (_property.type != SNamedPropertyElement::Type::STRING || type == Type::LDR_FILM) - { - error = true; - return; - } - static const core::unordered_map StringToType = - { - {"gamma", LDR::GAMMA}, - {"reinhard",LDR::REINHARD} - }; - auto found = StringToType.find(_property.svalue); - if (found != StringToType.end()) + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("variable",STRING,std::is_same,M) { - error = true; - return; + setLimitedString("variable",_this->outputFilePath,_property,logger); return true; } - ldrfilm.tonemapMethod = found->second; - }; - auto setGamma = SET_PROPERTY_TEMPLATE(gamma, SNamedPropertyElement::Type::FLOAT, LDR); - auto setExposure = SET_PROPERTY_TEMPLATE(exposure, SNamedPropertyElement::Type::FLOAT, LDR); - auto setKey = SET_PROPERTY_TEMPLATE(key, SNamedPropertyElement::Type::FLOAT, LDR); - auto setBurn = SET_PROPERTY_TEMPLATE(burn, SNamedPropertyElement::Type::FLOAT, LDR); - auto setDigits = SET_PROPERTY_TEMPLATE(digits, SNamedPropertyElement::Type::INTEGER, M); - auto setVariable = [&]() -> void - { - if (_property.type != SNamedPropertyElement::Type::STRING || type == Type::MFILM) + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("outputFilePath",STRING) { - error = true; - return; + setLimitedString("outputFilePath",_this->outputFilePath,_property,logger); return true; } - size_t len = std::min(strlen(_property.svalue),M::MaxVarNameLen); - memcpy(mfilm.variable,_property.svalue,len); - mfilm.variable[len] = 0; - }; - auto setOutputFilePath = [&]() -> void - { - if (_property.type != SNamedPropertyElement::Type::STRING) + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("bloomFilePath",STRING) { - error = true; - return; + setLimitedString("bloomFilePath",_this->denoiserTonemapperArgs,_property,logger); return true; } - - size_t len = std::min(strlen(_property.svalue),MaxPathLen); - memcpy(outputFilePath,_property.svalue,len); - outputFilePath[len] = 0; - }; - - auto setBloomFilePath = [&]() -> void - { - if (_property.type != SNamedPropertyElement::Type::STRING) + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("tonemapper",STRING) { - error = true; - return; + setLimitedString("tonemapper",_this->denoiserTonemapperArgs,_property,logger); return true; } + }); - size_t len = std::min(strlen(_property.svalue),MaxPathLen); - memcpy(denoiserBloomFilePath,_property.svalue,len); - denoiserBloomFilePath[len] = 0; - }; - - auto setTonemapperArgs = [&]() -> void - { - if (_property.type != SNamedPropertyElement::Type::STRING) - { - error = true; - return; - } + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(cascadeCount,INTEGER); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(cascadeLuminanceBase,FLOAT); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(cascadeLuminanceStart,FLOAT); - size_t len = std::min(strlen(_property.svalue),MaxTonemapperArgsLen); - memcpy(denoiserTonemapperArgs,_property.svalue,len); - denoiserTonemapperArgs[len] = 0; - }; + const auto floatPropType = SNamedPropertyElement::Type::FLOAT; + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(denoiserBloomScale,FLOAT); + retval.byPropertyType[floatPropType]["bloomScale"] = retval.byPropertyType[floatPropType]["denoiserBloomScale"]; // alias + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(denoiserBloomIntensity,FLOAT); + retval.byPropertyType[floatPropType]["bloomIntensity"] = retval.byPropertyType[floatPropType]["denoiserBloomIntensity"]; // alias - auto setCascadeCount = SET_PROPERTY(cascadeCount,SNamedPropertyElement::Type::INTEGER); - auto setCascadeLuminanceBase = SET_PROPERTY(cascadeLuminanceBase,SNamedPropertyElement::Type::FLOAT); - auto setCascadeLuminanceStart = SET_PROPERTY(cascadeLuminanceStart,SNamedPropertyElement::Type::FLOAT); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(envmapRegularizationFactor,FLOAT); - auto setBloomScale = SET_PROPERTY(denoiserBloomScale,SNamedPropertyElement::Type::FLOAT); - auto setBloomIntensity = SET_PROPERTY(denoiserBloomIntensity,SNamedPropertyElement::Type::FLOAT); - auto setEnvmapRegularizationFactor = SET_PROPERTY(envmapRegularizationFactor,SNamedPropertyElement::Type::FLOAT); - - const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> SetPropertyMap = - { - {"width", setWidth}, - {"height", setHeight}, - {"cropOffsetX", setCropOffsetX}, - {"cropOffsetY", setCropOffsetY}, - {"cropWidth", setCropWidth}, - {"cropHeight", setCropHeight}, - {"fileFormat", setFileFormat}, - {"pixelFormat", setPixelFormat}, - {"componentFormat", setComponentFormat}, - {"banner", setBanner}, - {"highQualityEdges", setHighQualityEdges}, - {"attachLog", setAttachLog}, - {"tonemapMethod", setTonemapMethod}, - {"gamma", setGamma}, - {"exposure", setExposure}, - {"key", setKey}, - {"burn", setBurn}, - {"digits", setDigits}, - {"variable", setVariable}, - {"outputFilePath", setOutputFilePath}, - {"bloomFilePath", setBloomFilePath}, - {"cascadeCount", setCascadeCount}, - {"cascadeLuminanceBase", setCascadeLuminanceBase}, - {"cascadeLuminanceStart", setCascadeLuminanceStart}, - {"bloomScale", setBloomScale}, - {"bloomIntensity", setBloomIntensity}, - {"tonemapper", setTonemapperArgs}, - {"envmapRegularizationFactor", setEnvmapRegularizationFactor} - }; - - auto found = SetPropertyMap.find(_property.name); - if (found == SetPropertyMap.end()) - { - _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("No Film can have such property set with name: " + _property.name+"\nRemember we don't support \"render-time annotations\""); - return false; - } - - found->second(); - return !error; + return retval; } -bool CElementFilm::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* metadata) +bool CElementFilm::onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) { cropOffsetX = std::max(cropOffsetX,0); cropOffsetY = std::max(cropOffsetY,0); @@ -339,8 +157,8 @@ bool CElementFilm::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override case PFM: break; default: - ParserLog::invalidXMLFileStructure(getLogName() + ": film type does not support this file format"); - _NBL_DEBUG_BREAK_IF(true); + invalidXMLFileStructure(logger,getLogName() + ": film type does not support this file format"); + return false; }; break; @@ -350,8 +168,8 @@ bool CElementFilm::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override case OPENEXR: break; default: - ParserLog::invalidXMLFileStructure(getLogName() + ": film type does not support this file format"); - _NBL_DEBUG_BREAK_IF(true); + invalidXMLFileStructure(logger,getLogName() + ": film type does not support this file format"); + return false; }; break; @@ -363,8 +181,8 @@ bool CElementFilm::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override case JPEG: break; default: - ParserLog::invalidXMLFileStructure(getLogName() + ": film type does not support this file format"); - _NBL_DEBUG_BREAK_IF(true); + invalidXMLFileStructure(logger,getLogName() + ": film type does not support this file format"); + return false; }; switch (pixelFormat) @@ -378,8 +196,8 @@ bool CElementFilm::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override case XYZ: [[fallthrough]]; case XYZA: - ParserLog::invalidXMLFileStructure(getLogName() + ": film type does not support this pixel format"); - _NBL_DEBUG_BREAK_IF(true); + invalidXMLFileStructure(logger,getLogName() + ": film type does not support this pixel format"); + return false; break; default: @@ -396,8 +214,8 @@ bool CElementFilm::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override case NUMPY: break; default: - ParserLog::invalidXMLFileStructure(getLogName() + ": film type does not support this file format"); - _NBL_DEBUG_BREAK_IF(true); + invalidXMLFileStructure(logger,getLogName() + ": film type does not support this file format"); + return false; }; switch (pixelFormat) @@ -405,8 +223,8 @@ bool CElementFilm::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override case XYZ: [[fallthrough]]; case XYZA: - ParserLog::invalidXMLFileStructure(getLogName() + ": film type does not support this pixel format"); - _NBL_DEBUG_BREAK_IF(true); + invalidXMLFileStructure(logger,getLogName() + ": film type does not support this pixel format"); + return false; break; default: @@ -417,14 +235,14 @@ bool CElementFilm::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override case FLOAT32: break; default: - ParserLog::invalidXMLFileStructure(getLogName() + ": film type does not support this component format"); - _NBL_DEBUG_BREAK_IF(true); + invalidXMLFileStructure(logger,getLogName() + ": film type does not support this component format"); + return false; }; break; default: - ParserLog::invalidXMLFileStructure(getLogName() + ": type not specified"); - _NBL_DEBUG_BREAK_IF(true); + invalidXMLFileStructure(logger,getLogName() + ": type not specified"); + return false; } @@ -432,6 +250,4 @@ bool CElementFilm::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementIntegrator.cpp b/src/nbl/ext/MitsubaLoader/CElementIntegrator.cpp index 7843cd30eb..f99b4487f9 100644 --- a/src/nbl/ext/MitsubaLoader/CElementIntegrator.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementIntegrator.cpp @@ -1,435 +1,226 @@ // 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/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" - +#include "nbl/ext/MitsubaLoader/CElementIntegrator.h" #include "nbl/ext/MitsubaLoader/CMitsubaMetadata.h" +#include "nbl/ext/MitsubaLoader/ElementMacros.h" + +#include "nbl/type_traits.h" // legacy stuff for `is_any_of` #include -namespace nbl + +namespace nbl::ext::MitsubaLoader { -namespace ext + +namespace impl { -namespace MitsubaLoader +template +struct has_strictNormals { - - -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) + constexpr static bool value = std::is_same_v || + std::is_base_of_v; +}; +template +struct has_granularity { - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr,""); - - static const core::unordered_map StringToType = - { - {"ao", CElementIntegrator::Type::AO}, - {"direct", CElementIntegrator::Type::DIRECT}, - {"path", CElementIntegrator::Type::PATH}, - {"volpath_simple", CElementIntegrator::Type::VOL_PATH_SIMPLE}, - {"volpath", CElementIntegrator::Type::VOL_PATH}, - {"bdpt", CElementIntegrator::Type::BDPT}, - {"photonmapper", CElementIntegrator::Type::PHOTONMAPPER}, - {"ppm", CElementIntegrator::Type::PPM}, - {"sppm", CElementIntegrator::Type::SPPM}, - {"pssmlt", CElementIntegrator::Type::PSSMLT}, - {"mlt", CElementIntegrator::Type::MLT}, - {"erpt", CElementIntegrator::Type::ERPT}, - {"ptracer", CElementIntegrator::Type::ADJ_P_TRACER}, - {"adaptive", CElementIntegrator::Type::ADAPTIVE}, - {"vpl", CElementIntegrator::Type::VPL}, - {"irrcache", CElementIntegrator::Type::IRR_CACHE}, - {"multichannel", CElementIntegrator::Type::MULTI_CHANNEL}, - {"field", CElementIntegrator::Type::FIELD_EXTRACT} - }; - - auto found = StringToType.find(type); - if (found==StringToType.end()) - { - ParserLog::invalidXMLFileStructure("unknown type"); - _NBL_DEBUG_BREAK_IF(false); - return CElementFactory::return_type(nullptr, ""); - } - - CElementIntegrator* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr, ""); - - obj->type = found->second; - // defaults - switch (obj->type) - { - case CElementIntegrator::Type::AO: - obj->ao = CElementIntegrator::AmbientOcclusion(); - break; - case CElementIntegrator::Type::DIRECT: - obj->direct = CElementIntegrator::DirectIllumination(); - break; - case CElementIntegrator::Type::PATH: - obj->path = CElementIntegrator::PathTracing(); - break; - case CElementIntegrator::Type::VOL_PATH_SIMPLE: - obj->volpath_simple = CElementIntegrator::SimpleVolumetricPathTracing(); - break; - case CElementIntegrator::Type::VOL_PATH: - obj->volpath = CElementIntegrator::ExtendedVolumetricPathTracing(); - break; - case CElementIntegrator::Type::BDPT: - obj->bdpt = CElementIntegrator::BiDirectionalPathTracing(); - break; - case CElementIntegrator::Type::PHOTONMAPPER: - obj->photonmapper = CElementIntegrator::PhotonMapping(); - break; - case CElementIntegrator::Type::PPM: - obj->ppm = CElementIntegrator::ProgressivePhotonMapping(); - break; - case CElementIntegrator::Type::SPPM: - obj->sppm = CElementIntegrator::StochasticProgressivePhotonMapping(); - break; - case CElementIntegrator::Type::PSSMLT: - obj->pssmlt = CElementIntegrator::PrimarySampleSpaceMetropolisLightTransport(); - break; - case CElementIntegrator::Type::MLT: - obj->mlt = CElementIntegrator::PathSpaceMetropolisLightTransport(); - break; - case CElementIntegrator::Type::ERPT: - obj->erpt = CElementIntegrator::EnergyRedistributionPathTracing(); - break; - case CElementIntegrator::Type::ADJ_P_TRACER: - obj->ptracer = CElementIntegrator::AdjointParticleTracing(); - break; - case CElementIntegrator::Type::ADAPTIVE: - obj->adaptive = CElementIntegrator::AdaptiveIntegrator(); - break; - case CElementIntegrator::Type::VPL: - obj->vpl = CElementIntegrator::VirtualPointLights(); - break; - case CElementIntegrator::Type::IRR_CACHE: - obj->irrcache = CElementIntegrator::IrradianceCacheIntegrator(); - break; - case CElementIntegrator::Type::MULTI_CHANNEL: - obj->multichannel = CElementIntegrator::MultiChannelIntegrator(); - break; - case CElementIntegrator::Type::FIELD_EXTRACT: - obj->field = CElementIntegrator::FieldExtraction(); - break; - default: - break; - } - return CElementFactory::return_type(obj, std::move(name)); + constexpr static bool value = std::is_base_of_v || + std::is_same_v; +}; +template +struct has_directSamples +{ + constexpr static bool value = std::is_same_v || + std::is_base_of_v || + std::is_same_v; +}; } -bool CElementIntegrator::addProperty(SNamedPropertyElement&& _property) +auto CElementIntegrator::compAddPropertyMap() -> AddPropertyMap { - bool error = false; - auto dispatch = [&](auto func) -> void - { - switch (type) + using this_t = CElementIntegrator; + AddPropertyMap retval; + + // common + // this one has really funny legacy behaviour which Mitsuba allowed contrary to its PDF docs + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("shadingSamples",INTEGER,is_any_of,AmbientOcclusion,DirectIllumination) + { + if (_this->type == Type::AO) + _this->ao.shadingSamples = _property.ivalue; + else + _this->direct.emitterSamples = _this->direct.bsdfSamples = _property.ivalue; + return true; + } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(strictNormals,BOOLEAN,impl::has_strictNormals); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(granularity,INTEGER,impl::has_granularity); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(directSamples,INTEGER,impl::has_directSamples); + + // ambient + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(rayLength,FLOAT,std::is_same,AmbientOcclusion); + + // emitter hideables + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(hideEmitters,BOOLEAN,derived_from,EmitterHideableBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(hideEnvironment,BOOLEAN,derived_from,EmitterHideableBase); + + // direct + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(emitterSamples,INTEGER,std::is_same,DirectIllumination); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(bsdfSamples,INTEGER,std::is_same,DirectIllumination); + // COMMON: strictNormals + + // monte carlo base + // Not using `NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED` because members have different names than XML names + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("maxDepth",INTEGER,derived_from,MonteCarloTracingBase) { - case CElementIntegrator::Type::AO: - func(ao); - break; - case CElementIntegrator::Type::DIRECT: - func(direct); - break; - case CElementIntegrator::Type::PATH: - func(path); - break; - case CElementIntegrator::Type::VOL_PATH_SIMPLE: - func(volpath_simple); - break; - case CElementIntegrator::Type::VOL_PATH: - func(volpath); - break; - case CElementIntegrator::Type::BDPT: - func(bdpt); - break; - case CElementIntegrator::Type::PHOTONMAPPER: - func(photonmapper); - break; - case CElementIntegrator::Type::PPM: - func(ppm); - break; - case CElementIntegrator::Type::SPPM: - func(sppm); - break; - case CElementIntegrator::Type::PSSMLT: - func(pssmlt); - break; - case CElementIntegrator::Type::MLT: - func(mlt); - break; - case CElementIntegrator::Type::ERPT: - func(erpt); - break; - case CElementIntegrator::Type::ADJ_P_TRACER: - func(ptracer); - break; - case CElementIntegrator::Type::ADAPTIVE: - func(adaptive); - break; - case CElementIntegrator::Type::VPL: - func(vpl); - break; - case CElementIntegrator::Type::IRR_CACHE: - func(irrcache); - break; - case CElementIntegrator::Type::MULTI_CHANNEL: - func(multichannel); - break; - case CElementIntegrator::Type::FIELD_EXTRACT: - func(field); - break; - default: - error = true; - break; + _this->path.maxPathDepth = _property.ivalue; + return true; } - }; - -#define SET_PROPERTY_TEMPLATE(MEMBER,PROPERTY_TYPE, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ - { \ - if (_property.type!=PROPERTY_TYPE) { \ - error = true; \ - return; \ - } \ - state. ## MEMBER = _property.getProperty(); \ - } \ - }); \ - } - - auto processRayLength = SET_PROPERTY_TEMPLATE(rayLength,SNamedPropertyElement::Type::FLOAT,AmbientOcclusion); - auto processEmitterSamples = SET_PROPERTY_TEMPLATE(emitterSamples,SNamedPropertyElement::Type::INTEGER,DirectIllumination); - auto processBSDFSamples = SET_PROPERTY_TEMPLATE(bsdfSamples,SNamedPropertyElement::Type::INTEGER,DirectIllumination); - auto processShadingSamples = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("rrDepth",INTEGER,derived_from,MonteCarloTracingBase) + { + _this->path.russianRouletteDepth = _property.ivalue; + return true; + } + ); + + // path tracing + // COMMON: strictNormals + + // bidirectional path tracing + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(lightImage,BOOLEAN,std::is_same,BiDirectionalPathTracing); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(sampleDirect,BOOLEAN,std::is_same,BiDirectionalPathTracing); + + // photon mapping base + // COMMON: granularity + + // photon mapping + // COMMON: directSamples + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(glossySamples,INTEGER,std::is_same,PhotonMapping); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(globalPhotons,INTEGER,std::is_same,PhotonMapping); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(causticPhotons,INTEGER,std::is_same,PhotonMapping); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(volumePhotons,INTEGER,std::is_same,PhotonMapping); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(globalLookupRadius,FLOAT,std::is_same,PhotonMapping); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(causticLookupRadius,FLOAT,std::is_same,PhotonMapping); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(lookupSize,INTEGER,std::is_same,PhotonMapping); + + // progressive + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(photonCount,INTEGER,derived_from,ProgressivePhotonMapping); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(initialRadius,FLOAT,derived_from,ProgressivePhotonMapping); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(alpha,FLOAT,derived_from,ProgressivePhotonMapping); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(maxPasses,INTEGER,derived_from,ProgressivePhotonMapping); + + // metropolis base + // COMMON: directSamples + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(luminanceSamples,INTEGER,derived_from,MetropolisLightTransportBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(twoStage,BOOLEAN,derived_from,MetropolisLightTransportBase); + + // primary sample space + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(bidirectional,BOOLEAN,std::is_same,PrimarySampleSpaceMetropolisLightTransport); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(pLarge,FLOAT,std::is_same,PrimarySampleSpaceMetropolisLightTransport); + + // permutable base + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(lensPerturbation,BOOLEAN,derived_from,PerturbateableBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(multiChainPerturbation,BOOLEAN,derived_from,PerturbateableBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(causticPerturbation,BOOLEAN,derived_from,PerturbateableBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(manifoldPerturbation,BOOLEAN,derived_from,PerturbateableBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(lambda,FLOAT,derived_from,PerturbateableBase); + + // path space metropolis + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(bidirectionalMutation,BOOLEAN,std::is_same,PathSpaceMetropolisLightTransport); + + // energy redistribution + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(numChains,FLOAT,std::is_same,EnergyRedistributionPathTracing); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(maxChains,FLOAT,std::is_same,EnergyRedistributionPathTracing); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(chainLength,INTEGER,std::is_same,EnergyRedistributionPathTracing); + // COMMON: directSamples + + // adjoint + // COMMON: granularity + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(bruteForce,BOOLEAN,std::is_same,AdjointParticleTracing); + + // vpl + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(maxPathDepth,INTEGER,std::is_same,VirtualPointLights); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(shadowMap,INTEGER,std::is_same,VirtualPointLights); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(clamping,FLOAT,std::is_same,VirtualPointLights); + + // field extraction + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("field",STRING,std::is_same,FieldExtraction) + { + static const core::unordered_map StringToType = { - if (_property.type!=SNamedPropertyElement::Type::INTEGER) - { - error = true; - return; - } - state.shadingSamples = _property.getProperty(); - } + {"position",FieldExtraction::Type::POSITION}, + {"relPosition",FieldExtraction::Type::RELATIVE_POSITION}, + {"distance",FieldExtraction::Type::DISTANCE}, + {"geoNormal",FieldExtraction::Type::GEOMETRIC_NORMAL}, + {"shNormal",FieldExtraction::Type::SHADING_NORMAL}, + {"uv",FieldExtraction::Type::UV_COORD}, + {"albedo",FieldExtraction::Type::ALBEDO}, + {"shapeIndex",FieldExtraction::Type::SHAPE_INDEX}, + {"primIndex",FieldExtraction::Type::PRIMITIVE_INDEX} + }; + auto found = StringToType.find(_property.svalue); + if (found!=StringToType.end()) + _this->field.field = found->second; else - { - if constexpr (std::is_same::value) - { - processEmitterSamples(); - processBSDFSamples(); - } - } - }); - }; - auto processStrictNormals = SET_PROPERTY_TEMPLATE(strictNormals,SNamedPropertyElement::Type::BOOLEAN,DirectIllumination,PathTracing); - auto processHideEmitters = SET_PROPERTY_TEMPLATE(hideEmitters,SNamedPropertyElement::Type::BOOLEAN,DirectIllumination,PathTracing,PhotonMapping); - auto processHideEnvironment = SET_PROPERTY_TEMPLATE(hideEnvironment,SNamedPropertyElement::Type::BOOLEAN,DirectIllumination,PathTracing,PhotonMapping); -#define ALL_PHOTONMAPPING_TYPES PhotonMapping,ProgressivePhotonMapping,StochasticProgressivePhotonMapping -#define ALL_MLT_TYPES PrimarySampleSpaceMetropolisLightTransport,PathSpaceMetropolisLightTransport -#define ALL_MC_TYPES PathTracing,SimpleVolumetricPathTracing,ExtendedVolumetricPathTracing,BiDirectionalPathTracing, \ - ALL_PHOTONMAPPING_TYPES,ALL_MLT_TYPES,EnergyRedistributionPathTracing,AdjointParticleTracing - auto processMaxDepth = SET_PROPERTY_TEMPLATE(maxPathDepth,SNamedPropertyElement::Type::INTEGER,ALL_MC_TYPES, VirtualPointLights); - auto processRRDepth = SET_PROPERTY_TEMPLATE(russianRouletteDepth,SNamedPropertyElement::Type::INTEGER,ALL_MC_TYPES); -#undef ALL_MC_TYPES - auto processLightImage = SET_PROPERTY_TEMPLATE(lightImage,SNamedPropertyElement::Type::BOOLEAN,BiDirectionalPathTracing); - auto processSampleDirect = SET_PROPERTY_TEMPLATE(sampleDirect,SNamedPropertyElement::Type::BOOLEAN,BiDirectionalPathTracing); - auto processGranularity = SET_PROPERTY_TEMPLATE(granularity,SNamedPropertyElement::Type::INTEGER,ALL_PHOTONMAPPING_TYPES,AdjointParticleTracing); -#undef ALL_PHOTONMAPPING_TYPES - auto processDirectSamples = SET_PROPERTY_TEMPLATE(directSamples,SNamedPropertyElement::Type::INTEGER,PhotonMapping,ALL_MLT_TYPES,EnergyRedistributionPathTracing); - auto processGlossySamples = SET_PROPERTY_TEMPLATE(glossySamples,SNamedPropertyElement::Type::INTEGER,PhotonMapping); - auto processGlobalPhotons = SET_PROPERTY_TEMPLATE(globalPhotons,SNamedPropertyElement::Type::INTEGER,PhotonMapping); - auto processCausticPhotons = SET_PROPERTY_TEMPLATE(causticPhotons,SNamedPropertyElement::Type::INTEGER,PhotonMapping); - auto processVolumePhotons = SET_PROPERTY_TEMPLATE(volumePhotons,SNamedPropertyElement::Type::INTEGER,PhotonMapping); - auto processGlobalLookupRadius = SET_PROPERTY_TEMPLATE(globalLURadius,SNamedPropertyElement::Type::FLOAT,PhotonMapping); - auto processCausticLookupRadius = SET_PROPERTY_TEMPLATE(causticLURadius,SNamedPropertyElement::Type::FLOAT,PhotonMapping); - auto processLookupSize = SET_PROPERTY_TEMPLATE(LUSize,SNamedPropertyElement::Type::INTEGER,PhotonMapping); - auto processPhotonCount = SET_PROPERTY_TEMPLATE(photonCount,SNamedPropertyElement::Type::INTEGER,ProgressivePhotonMapping,StochasticProgressivePhotonMapping); - auto processInitialRadius = SET_PROPERTY_TEMPLATE(initialRadius,SNamedPropertyElement::Type::FLOAT,ProgressivePhotonMapping,StochasticProgressivePhotonMapping); - auto processAlpha = SET_PROPERTY_TEMPLATE(alpha,SNamedPropertyElement::Type::FLOAT,ProgressivePhotonMapping,StochasticProgressivePhotonMapping); - auto processMaxPasses = SET_PROPERTY_TEMPLATE(maxPasses,SNamedPropertyElement::Type::INTEGER,ProgressivePhotonMapping,StochasticProgressivePhotonMapping); - auto processLuminanceSamples = SET_PROPERTY_TEMPLATE(luminanceSamples,SNamedPropertyElement::Type::INTEGER,ALL_MLT_TYPES); - auto processTwoStage = SET_PROPERTY_TEMPLATE(twoStage,SNamedPropertyElement::Type::BOOLEAN,ALL_MLT_TYPES); -#undef ALL_MLT_TYPES - auto processBidirectional = SET_PROPERTY_TEMPLATE(bidirectional,SNamedPropertyElement::Type::BOOLEAN,PrimarySampleSpaceMetropolisLightTransport); - auto processPLarge = SET_PROPERTY_TEMPLATE(pLarge,SNamedPropertyElement::Type::FLOAT,PrimarySampleSpaceMetropolisLightTransport); - auto processLensPerturbation = SET_PROPERTY_TEMPLATE(lensPerturbation,SNamedPropertyElement::Type::BOOLEAN,PathSpaceMetropolisLightTransport,EnergyRedistributionPathTracing); - auto processMultiChainPerturbation = SET_PROPERTY_TEMPLATE(multiChainPerturbation,SNamedPropertyElement::Type::BOOLEAN,PathSpaceMetropolisLightTransport,EnergyRedistributionPathTracing); - auto processCausticPerturbation = SET_PROPERTY_TEMPLATE(causticPerturbation,SNamedPropertyElement::Type::BOOLEAN,PathSpaceMetropolisLightTransport,EnergyRedistributionPathTracing); - auto processManifoldPerturbation = SET_PROPERTY_TEMPLATE(manifoldPerturbation,SNamedPropertyElement::Type::BOOLEAN,PathSpaceMetropolisLightTransport,EnergyRedistributionPathTracing); - auto processLambda = SET_PROPERTY_TEMPLATE(lambda,SNamedPropertyElement::Type::FLOAT,PathSpaceMetropolisLightTransport,EnergyRedistributionPathTracing); - auto processBidirectionalMutation = SET_PROPERTY_TEMPLATE(bidirectionalMutation,SNamedPropertyElement::Type::BOOLEAN,PathSpaceMetropolisLightTransport); - auto processNumChains = SET_PROPERTY_TEMPLATE(numChains,SNamedPropertyElement::Type::FLOAT,EnergyRedistributionPathTracing); - auto processMaxChains = SET_PROPERTY_TEMPLATE(maxChains,SNamedPropertyElement::Type::FLOAT,EnergyRedistributionPathTracing); - auto processChainLength = SET_PROPERTY_TEMPLATE(chainLength,SNamedPropertyElement::Type::INTEGER,EnergyRedistributionPathTracing); - auto processBruteForce = SET_PROPERTY_TEMPLATE(bruteForce,SNamedPropertyElement::Type::BOOLEAN,AdjointParticleTracing); - auto processShadowMap = SET_PROPERTY_TEMPLATE(shadowMap,SNamedPropertyElement::Type::INTEGER,VirtualPointLights); - auto processClamping = SET_PROPERTY_TEMPLATE(clamping,SNamedPropertyElement::Type::FLOAT,VirtualPointLights); - auto processField = [&]() -> void - { - dispatch([&](auto& state) -> void + _this->field.field = FieldExtraction::Type::INVALID; + return true; + } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("undefined",FLOAT,std::is_same,FieldExtraction) { - using state_type = std::remove_reference::type; - if constexpr (std::is_same::value) - { - if (_property.type != SNamedPropertyElement::Type::STRING) - { - error = true; - return; - } - static const core::unordered_map StringToType = - { - {"position",FieldExtraction::Type::POSITION}, - {"relPosition",FieldExtraction::Type::RELATIVE_POSITION}, - {"distance",FieldExtraction::Type::DISTANCE}, - {"geoNormal",FieldExtraction::Type::GEOMETRIC_NORMAL}, - {"shNormal",FieldExtraction::Type::SHADING_NORMAL}, - {"uv",FieldExtraction::Type::UV_COORD}, - {"albedo",FieldExtraction::Type::ALBEDO}, - {"shapeIndex",FieldExtraction::Type::SHAPE_INDEX}, - {"primIndex",FieldExtraction::Type::PRIMITIVE_INDEX} - }; - auto found = StringToType.find(_property.svalue); - if (found!=StringToType.end()) - state.field = found->second; - else - state.field = FieldExtraction::Type::INVALID; - } - }); - }; - auto processUndefined = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; + _this->field.undefined = _property; + return true; + } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("undefined",SPECTRUM,std::is_same,FieldExtraction) + { + _this->field.undefined = _property; + return true; + } + ); - if constexpr (std::is_same::value) - { - if (_property.type != SNamedPropertyElement::Type::FLOAT && _property.type != SNamedPropertyElement::Type::SPECTRUM) - { - error = true; - return; - } - state.undefined = _property; // TODO: redo - } - }); - }; - auto processMaxError = SET_PROPERTY_TEMPLATE(maxError,SNamedPropertyElement::Type::FLOAT,AdaptiveIntegrator); - auto processPValue = SET_PROPERTY_TEMPLATE(pValue,SNamedPropertyElement::Type::FLOAT,AdaptiveIntegrator); - auto processMaxSampleFactor = SET_PROPERTY_TEMPLATE(maxSampleFactor,SNamedPropertyElement::Type::INTEGER,AdaptiveIntegrator); - auto processResolution = SET_PROPERTY_TEMPLATE(resolution,SNamedPropertyElement::Type::INTEGER,IrradianceCacheIntegrator); - auto processQuality = SET_PROPERTY_TEMPLATE(quality,SNamedPropertyElement::Type::FLOAT,IrradianceCacheIntegrator); - auto processGradients = SET_PROPERTY_TEMPLATE(gradients,SNamedPropertyElement::Type::BOOLEAN,IrradianceCacheIntegrator); - auto processClampNeighbour = SET_PROPERTY_TEMPLATE(clampNeighbour,SNamedPropertyElement::Type::BOOLEAN,IrradianceCacheIntegrator); - auto processClampScreen = SET_PROPERTY_TEMPLATE(clampScreen,SNamedPropertyElement::Type::BOOLEAN,IrradianceCacheIntegrator); - auto processOverture = SET_PROPERTY_TEMPLATE(overture,SNamedPropertyElement::Type::BOOLEAN,IrradianceCacheIntegrator); - auto processQualityAdjustment = SET_PROPERTY_TEMPLATE(qualityAdjustment,SNamedPropertyElement::Type::FLOAT,IrradianceCacheIntegrator); - auto processIndirecOnly = SET_PROPERTY_TEMPLATE(indirectOnly,SNamedPropertyElement::Type::BOOLEAN,IrradianceCacheIntegrator); - auto processDebug = SET_PROPERTY_TEMPLATE(debug,SNamedPropertyElement::Type::BOOLEAN,IrradianceCacheIntegrator); - - const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> SetPropertyMap = - { - {"shadingSamples",processShadingSamples}, - {"rayLength",processRayLength}, - {"emitterSamples",processEmitterSamples}, - {"bsdfSamples",processBSDFSamples}, - {"strictNormals",processStrictNormals}, - {"hideEmitters",processHideEmitters}, - {"hideEnvironment",processHideEnvironment}, - {"maxDepth",processMaxDepth}, - {"rrDepth",processRRDepth}, - {"lightImage",processLightImage}, - {"sampleDirect",processSampleDirect}, - {"granularity",processGranularity}, - {"directSamples",processDirectSamples}, - {"glossySamples",processGlossySamples}, - {"globalPhotons",processGlobalPhotons}, - {"causticPhotons",processCausticPhotons}, - {"volumePhotons",processVolumePhotons}, - {"globalLookupRadius",processGlobalLookupRadius}, - {"causticLookupRadius",processCausticLookupRadius}, - {"lookupSize",processLookupSize}, - {"photonCount",processPhotonCount}, - {"initialRadius",processInitialRadius}, - {"alpha",processAlpha}, - {"maxPasses",processMaxPasses}, - {"luminanceSamples",processLuminanceSamples}, - {"twoStage",processTwoStage}, - {"bidirectional",processBidirectional}, - {"pLarge",processPLarge}, - {"lensPerturbation",processLensPerturbation}, - {"multiChainPerturbation",processMultiChainPerturbation}, - {"causticPerturbation",processCausticPerturbation}, - {"manifoldPerturbation",processManifoldPerturbation}, - {"lambda",processLambda}, - {"bidirectionalMutation",processBidirectionalMutation}, - {"numChains",processNumChains}, - {"maxChains",processMaxChains}, - {"chainLength",processChainLength}, - {"bruteForce",processBruteForce}, - {"shadowMap",processShadowMap}, - {"clamping",processClamping}, - {"field",processField}, - {"undefined",processUndefined}, - {"maxError",processMaxError}, - {"pValue",processPValue}, - {"maxSampleFactor",processMaxSampleFactor}, - {"resolution",processResolution}, - {"quality",processQuality}, - {"gradients",processGradients}, - {"clampNeighbour",processClampNeighbour}, - {"clampScreen",processClampScreen}, - {"overture",processOverture}, - {"qualityAdjustment",processQualityAdjustment}, - {"indirectOnly",processIndirecOnly}, - {"debug",processDebug}, - }; - + // Now for the compound/nested integrators + // meta integrator has no members settable via properties - auto found = SetPropertyMap.find(_property.name); - if (found==SetPropertyMap.end()) - { - _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("No Integrator can have such property set with name: "+_property.name); - return false; - } + // adaptive integrator + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(maxError,FLOAT,std::is_same,AdaptiveIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(pValue,FLOAT,std::is_same,AdaptiveIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(maxSampleFactor,INTEGER,std::is_same,AdaptiveIntegrator); - found->second(); - return !error; + // irradiance cache + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(resolution,INTEGER,std::is_same,IrradianceCacheIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(quality,FLOAT,std::is_same,IrradianceCacheIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(gradients,BOOLEAN,std::is_same,IrradianceCacheIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(clampNeighbour,BOOLEAN,std::is_same,IrradianceCacheIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(clampScreen,BOOLEAN,std::is_same,IrradianceCacheIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(overture,BOOLEAN,std::is_same,IrradianceCacheIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(qualityAdjustment,FLOAT,std::is_same,IrradianceCacheIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(indirectOnly,BOOLEAN,std::is_same,IrradianceCacheIntegrator); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(debug,BOOLEAN,std::is_same,IrradianceCacheIntegrator); + + // multi channel no extra members + + + return retval; } -bool CElementIntegrator::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* metadata) +bool CElementIntegrator::onEndTag(CMitsubaMetadata* metadata, system::logger_opt_ptr logger) { - if (type == Type::INVALID) - { - ParserLog::invalidXMLFileStructure(getLogName() + ": type not specified"); - _NBL_DEBUG_BREAK_IF(true); - return true; - } + NBL_EXT_MITSUBA_LOADER_ELEMENT_INVALID_TYPE_CHECK(true); // TODO: Validation { } - if (metadata->m_global.m_integrator.type!=Type::INVALID) + /* Nested integrators rely on closing branch to post-order visit here to set the root one as the main one in the metadata + if (metadata->m_global.m_integrator.type!=Type::INVALID) // D { - ParserLog::invalidXMLFileStructure(getLogName() + ": already specified an integrator"); - _NBL_DEBUG_BREAK_IF(true); + invalidXMLFileStructure(logger,"already specified an integrator, NOT overwriting."); return true; - } + }*/ metadata->m_global.m_integrator = *this; return true; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementRFilter.cpp b/src/nbl/ext/MitsubaLoader/CElementRFilter.cpp index 160fd5aadb..9bd4b864df 100644 --- a/src/nbl/ext/MitsubaLoader/CElementRFilter.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementRFilter.cpp @@ -1,139 +1,43 @@ // 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/core/string/stringutil.h" +#include "nbl/ext/MitsubaLoader/CElementRFilter.h" #include "nbl/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ +#include "nbl/ext/MitsubaLoader/ElementMacros.h" -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) +namespace nbl::ext::MitsubaLoader { - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr, ""); - - static const core::unordered_map StringToType = - { - std::make_pair("box", CElementRFilter::Type::BOX), - std::make_pair("tent", CElementRFilter::Type::TENT), - std::make_pair("gaussian", CElementRFilter::Type::GAUSSIAN), - std::make_pair("mitchell", CElementRFilter::Type::MITCHELL), - std::make_pair("catmullrom", CElementRFilter::Type::CATMULLROM), - std::make_pair("lanczos", CElementRFilter::Type::LANCZOS) - }; - - auto found = StringToType.find(type); - if (found==StringToType.end()) - { - ParserLog::invalidXMLFileStructure("unknown type"); - _NBL_DEBUG_BREAK_IF(false); - return CElementFactory::return_type(nullptr, ""); - } + +auto CElementRFilter::compAddPropertyMap() -> AddPropertyMap +{ + using this_t = CElementRFilter; + AddPropertyMap retval; + + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(sigma,FLOAT,std::is_same,Gaussian); - CElementRFilter* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr, ""); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(B,FLOAT,is_any_of,MitchellNetravali,CatmullRom); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(C,FLOAT,is_any_of,MitchellNetravali,CatmullRom); - obj->type = found->second; - //validation - switch (obj->type) - { - case CElementRFilter::Type::BOX: - [[fallthrough]]; - case CElementRFilter::Type::TENT: - break; - case CElementRFilter::Type::GAUSSIAN: - obj->gaussian = CElementRFilter::Gaussian(); - break; - case CElementRFilter::Type::MITCHELL: - obj->mitchell = CElementRFilter::MitchellNetravali(); - break; - case CElementRFilter::Type::CATMULLROM: - obj->catmullrom = CElementRFilter::MitchellNetravali(); - break; - case CElementRFilter::Type::LANCZOS: - obj->lanczos = CElementRFilter::LanczosSinc(); - break; - default: - break; - } - return CElementFactory::return_type(obj, std::move(name)); -} + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(lobes,INTEGER,std::is_same,LanczosSinc); -bool CElementRFilter::addProperty(SNamedPropertyElement&& _property) -{ - if (_property.type == SNamedPropertyElement::Type::INTEGER) - { - if (core::strcmpi(_property.name,std::string("lobes"))) - { - ParserLog::invalidXMLFileStructure("\"lobes\" must be an integer property"); - _NBL_DEBUG_BREAK_IF(true); - return false; - } - lanczos.lobes = _property.ivalue; - return true; - } - else - if (_property.type == SNamedPropertyElement::Type::FLOAT) - { - if (core::strcmpi(_property.name,std::string("b"))==0) - { - mitchell.B = _property.fvalue; - return true; - } - else if (core::strcmpi(_property.name,std::string("c"))==0) - { - mitchell.C = _property.fvalue; - return true; - } - else if (core::strcmpi(_property.name,std::string("kappa"))==0) - { - kappa = _property.fvalue; - return true; - } - else if (core::strcmpi(_property.name,std::string("Emin"))==0) - { - Emin = _property.fvalue; - return true; - } - else - ParserLog::invalidXMLFileStructure("unsupported rfilter property called: "+_property.name); - } - else - { - ParserLog::invalidXMLFileStructure("this reconstruction filter type does not take this parameter type for parameter: " + _property.name); - _NBL_DEBUG_BREAK_IF(true); - } + // common + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(kappa,FLOAT); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(Emin,FLOAT); - return false; + return retval; } -bool CElementRFilter::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) +bool CElementRFilter::onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) { - if (type == Type::INVALID) - { - ParserLog::invalidXMLFileStructure(getLogName() + ": type not specified"); - _NBL_DEBUG_BREAK_IF(true); - return true; - } + NBL_EXT_MITSUBA_LOADER_ELEMENT_INVALID_TYPE_CHECK(true); // TODO: Validation return true; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementSampler.cpp b/src/nbl/ext/MitsubaLoader/CElementSampler.cpp index 01306d2201..b862a754fb 100644 --- a/src/nbl/ext/MitsubaLoader/CElementSampler.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementSampler.cpp @@ -1,138 +1,78 @@ -// 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/ext/MitsubaLoader/CElementSampler.h" #include "nbl/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" - -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ +#include "nbl/ext/MitsubaLoader/ElementMacros.h" -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) +namespace nbl::ext::MitsubaLoader { - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr, ""); - - static const core::unordered_map StringToType = - { - std::make_pair("independent", CElementSampler::Type::INDEPENDENT), - std::make_pair("stratified", CElementSampler::Type::STRATIFIED), - std::make_pair("ldsampler", CElementSampler::Type::LDSAMPLER), - std::make_pair("halton", CElementSampler::Type::HALTON), - std::make_pair("hammersley", CElementSampler::Type::HAMMERSLEY), - std::make_pair("sobol", CElementSampler::Type::SOBOL) - }; - - auto found = StringToType.find(type); - if (found==StringToType.end()) - { - ParserLog::invalidXMLFileStructure("unknown type"); - _NBL_DEBUG_BREAK_IF(false); - return CElementFactory::return_type(nullptr, ""); - } - - CElementSampler* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr, ""); - - obj->type = found->second; - obj->sampleCount = 4; - //validation - switch (obj->type) - { - case CElementSampler::Type::STRATIFIED: - [[fallthrough]]; - case CElementSampler::Type::LDSAMPLER: - obj->dimension = 4; - break; - case CElementSampler::Type::HALTON: - [[fallthrough]]; - case CElementSampler::Type::HAMMERSLEY: - obj->scramble = -1; - break; - case CElementSampler::Type::SOBOL: - obj->scramble = 0; - break; - default: - break; - } - return CElementFactory::return_type(obj, std::move(name)); -} - -bool CElementSampler::addProperty(SNamedPropertyElement&& _property) + +auto CElementSampler::compAddPropertyMap() -> AddPropertyMap { - if (_property.type == SNamedPropertyElement::Type::INTEGER && - _property.name == "sampleCount") - { - sampleCount = _property.ivalue; - switch (type) + using this_t = CElementSampler; + AddPropertyMap retval; + + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("sampleCount",INTEGER) { - case Type::STRATIFIED: - sampleCount = ceilf(sqrtf(sampleCount)); - break; - case Type::LDSAMPLER: - //sampleCount = core::roundUpToPoT(sampleCount); - break; - default: - break; + auto& sampleCount = _this->sampleCount; + sampleCount = _property.ivalue; + switch (_this->type) + { + case Type::STRATIFIED: + sampleCount = ceilf(sqrtf(sampleCount)); + break; + case Type::LDSAMPLER: + //sampleCount = core::roundUpToPoT(sampleCount); + break; + default: + break; + } + return true; } - } - else - if (_property.type == SNamedPropertyElement::Type::INTEGER && - _property.name == "dimension") - { - dimension = _property.ivalue; - if (type == Type::INDEPENDENT || type == Type::HALTON || type == Type::HAMMERSLEY) + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("dimension",INTEGER) { - ParserLog::invalidXMLFileStructure("this sampler type does not take these parameters"); - _NBL_DEBUG_BREAK_IF(true); - return false; + _this->dimension = _property.ivalue; + switch (_this->type) + { + case Type::INDEPENDENT: [[fallthrough]]; + case Type::HALTON: [[fallthrough]]; + case Type::HAMMERSLEY: + invalidXMLFileStructure(logger,"this sampler type ("+std::to_string(_this->type)+") does not take these parameters"); + return false; + default: + return true; + } } - } - else - if (_property.type == SNamedPropertyElement::Type::INTEGER && - _property.name == "scramble") - { - scramble = _property.ivalue; - if (type==Type::INDEPENDENT || type==Type::STRATIFIED || type == Type::LDSAMPLER) + }); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY("scramble",INTEGER) { - ParserLog::invalidXMLFileStructure("this sampler type does not take these parameters"); - _NBL_DEBUG_BREAK_IF(true); - return false; + _this->scramble = _property.ivalue; + switch (_this->type) + { + case Type::INDEPENDENT: [[fallthrough]]; + case Type::HALTON: [[fallthrough]]; + case Type::HAMMERSLEY: + invalidXMLFileStructure(logger,"this sampler type ("+std::to_string(_this->type)+") does not take these parameters"); + return false; + default: + return true; + } } - } - else - { - _NBL_DEBUG_BREAK_IF(true); - return false; - } + }); - return true; + return retval; } -bool CElementSampler::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) +bool CElementSampler::onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) { - if (type == Type::INVALID) - { - ParserLog::invalidXMLFileStructure(getLogName() + ": type not specified"); - _NBL_DEBUG_BREAK_IF(true); - return true; - } + NBL_EXT_MITSUBA_LOADER_ELEMENT_INVALID_TYPE_CHECK(true); // TODO: Validation return true; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementSensor.cpp b/src/nbl/ext/MitsubaLoader/CElementSensor.cpp index 06d8c53737..d8ab85bb26 100644 --- a/src/nbl/ext/MitsubaLoader/CElementSensor.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementSensor.cpp @@ -1,253 +1,91 @@ // 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/ext/MitsubaLoader/CElementSensor.h" #include "nbl/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" +#include "nbl/ext/MitsubaLoader/ElementMacros.h" #include -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ - -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) -{ - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr,""); - - static const core::unordered_map StringToType = - { - {"perspective", CElementSensor::Type::PERSPECTIVE}, - {"thinlens", CElementSensor::Type::THINLENS}, - {"orthographic", CElementSensor::Type::ORTHOGRAPHIC}, - {"telecentric", CElementSensor::Type::TELECENTRIC}, - {"spherical", CElementSensor::Type::SPHERICAL}, - {"irradiancemeter", CElementSensor::Type::IRRADIANCEMETER}, - {"radiancemeter", CElementSensor::Type::RADIANCEMETER}, - {"fluencemeter", CElementSensor::Type::FLUENCEMETER}/*, - {"perspective_rdist", CElementSensor::PERSPECTIVE_RDIST}*/ - }; - - auto found = StringToType.find(type); - if (found==StringToType.end()) - { - ParserLog::invalidXMLFileStructure("unknown type"); - _NBL_DEBUG_BREAK_IF(false); - return CElementFactory::return_type(nullptr, ""); - } - - CElementSensor* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr, ""); - obj->type = found->second; - // defaults - switch (obj->type) - { - case CElementSensor::Type::PERSPECTIVE: - obj->perspective = CElementSensor::PerspectivePinhole(); - break; - case CElementSensor::Type::THINLENS: - obj->thinlens = CElementSensor::PerspectiveThinLens(); - break; - case CElementSensor::Type::ORTHOGRAPHIC: - obj->orthographic = CElementSensor::Orthographic(); - break; - case CElementSensor::Type::TELECENTRIC: - obj->telecentric = CElementSensor::TelecentricLens(); - break; - case CElementSensor::Type::SPHERICAL: - obj->spherical = CElementSensor::SphericalCamera(); - break; - case CElementSensor::Type::IRRADIANCEMETER: - obj->irradiancemeter = CElementSensor::IrradianceMeter(); - break; - case CElementSensor::Type::RADIANCEMETER: - obj->radiancemeter = CElementSensor::RadianceMeter(); - break; - case CElementSensor::Type::FLUENCEMETER: - obj->fluencemeter = CElementSensor::FluenceMeter(); - break; - default: - break; - } - return CElementFactory::return_type(obj, std::move(name)); -} +namespace nbl::ext::MitsubaLoader +{ -bool CElementSensor::addProperty(SNamedPropertyElement&& _property) +auto CElementSensor::compAddPropertyMap() -> AddPropertyMap { - bool error = false; - auto dispatch = [&](auto func) -> void - { - switch (type) + using this_t = CElementSensor; + AddPropertyMap retval; + + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(up,VECTOR,derived_from,ShutterSensor); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(shiftX,FLOAT,derived_from,PerspectivePinhole); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(shiftY,FLOAT,derived_from,PerspectivePinhole); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(fov,FLOAT,derived_from,PerspectivePinhole); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("fovAxis",STRING,derived_from,PerspectivePinhole) { - case CElementSensor::Type::PERSPECTIVE: - func(perspective); - break; - case CElementSensor::Type::THINLENS: - func(thinlens); - break; - case CElementSensor::Type::ORTHOGRAPHIC: - func(orthographic); - break; - case CElementSensor::Type::TELECENTRIC: - func(telecentric); - break; - case CElementSensor::Type::SPHERICAL: - func(spherical); - break; - case CElementSensor::Type::IRRADIANCEMETER: - func(irradiancemeter); - break; - case CElementSensor::Type::RADIANCEMETER: - func(radiancemeter); - break; - case CElementSensor::Type::FLUENCEMETER: - func(fluencemeter); - break; - default: - error = true; - break; + auto& state = _this->perspective; + // TODO: check if this gives problem with delay loads + static const core::unordered_map StringToType = + { + {"x", PerspectivePinhole::FOVAxis::X}, + {"y", PerspectivePinhole::FOVAxis::Y}, + {"diagonal",PerspectivePinhole::FOVAxis::DIAGONAL}, + {"smaller", PerspectivePinhole::FOVAxis::SMALLER}, + {"larger", PerspectivePinhole::FOVAxis::LARGER} + }; + auto found = StringToType.find(_property.svalue); + if (found!=StringToType.end()) + state.fovAxis = found->second; + else + state.fovAxis = PerspectivePinhole::FOVAxis::INVALID; + return true; } - }; - -#define SET_PROPERTY_TEMPLATE(MEMBER,PROPERTY_TYPE,BASE) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (std::is_base_of::type >::value) \ - { \ - if (_property.type!=PROPERTY_TYPE) { \ - error = true; \ - return; \ - } \ - state. ## MEMBER = _property.getProperty(); \ - } \ - }); \ - } - - auto setUp = SET_PROPERTY_TEMPLATE(up,SNamedPropertyElement::Type::VECTOR,ShutterSensor); - auto setClipPlane = [&]() -> void + ); + + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(shutterOpen,FLOAT,derived_from,ShutterSensor); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(shutterClose,FLOAT,derived_from,ShutterSensor); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(moveSpeed,FLOAT,derived_from,ShutterSensor); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(zoomSpeed,FLOAT,derived_from,ShutterSensor); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(rotateSpeed,FLOAT,derived_from,ShutterSensor); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(nearClip,FLOAT,derived_from,CameraBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(farClip,FLOAT,derived_from,CameraBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(focusDistance,FLOAT,derived_from,DepthOfFieldBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(apertureRadius,FLOAT,derived_from,DepthOfFieldBase); + + // special + auto setClipPlane = [](this_t* _this, SNamedPropertyElement&& _property, const system::logger_opt_ptr logger)->bool { - dispatch([&](auto& state) -> void + if (_property.getVectorDimension()!=4) { - if (_property.type!=SNamedPropertyElement::Type::VECTOR || _property.getVectorDimension()==4) - { - error = true; - return; - } - constexpr std::string_view Name = "clipPlane"; - const std::string_view sv(_property.name); - if (sv.length()!=Name.length()+1 || sv.find(Name)!=0) - { - error = true; - return; - } - const auto index = std::atoi(sv.data()+Name.length()); - if (index>MaxClipPlanes) - { - error = true; - return; - } - state.clipPlanes[index] = _property.vvalue; - }); - }; - auto setShiftX = SET_PROPERTY_TEMPLATE(shiftX,SNamedPropertyElement::Type::FLOAT,PerspectivePinhole); - auto setShiftY = SET_PROPERTY_TEMPLATE(shiftY,SNamedPropertyElement::Type::FLOAT,PerspectivePinhole); - auto setFov = SET_PROPERTY_TEMPLATE(fov,SNamedPropertyElement::Type::FLOAT,PerspectivePinhole); - auto setFovAxis = [&]() -> void - { - dispatch([&](auto& state) -> void + return false; + } + constexpr std::string_view Name = "clipPlane"; + const std::string_view sv(_property.name); + if (sv.length()!=Name.length()+1 || sv.find(Name)!=0) { - using state_type = std::remove_reference::type; - if constexpr (std::is_base_of::value) - { - if (_property.type!=SNamedPropertyElement::Type::STRING) - { - error = true; - return; - } - static const core::unordered_map StringToType = - { - {"x", PerspectivePinhole::FOVAxis::X}, - {"y", PerspectivePinhole::FOVAxis::Y}, - {"diagonal",PerspectivePinhole::FOVAxis::DIAGONAL}, - {"smaller", PerspectivePinhole::FOVAxis::SMALLER}, - {"larger", PerspectivePinhole::FOVAxis::LARGER} - }; - auto found = StringToType.find(_property.svalue); - if (found!=StringToType.end()) - state.fovAxis = found->second; - else - state.fovAxis = PerspectivePinhole::FOVAxis::INVALID; - } - }); - }; - auto setShutterOpen = SET_PROPERTY_TEMPLATE(shutterOpen,SNamedPropertyElement::Type::FLOAT,ShutterSensor); - auto setShutterClose = SET_PROPERTY_TEMPLATE(shutterClose,SNamedPropertyElement::Type::FLOAT,ShutterSensor); - auto setMoveSpeed = SET_PROPERTY_TEMPLATE(moveSpeed,SNamedPropertyElement::Type::FLOAT,ShutterSensor); - auto setZoomSpeed = SET_PROPERTY_TEMPLATE(zoomSpeed,SNamedPropertyElement::Type::FLOAT,ShutterSensor); - auto setRotateSpeed = SET_PROPERTY_TEMPLATE(rotateSpeed,SNamedPropertyElement::Type::FLOAT,ShutterSensor); - auto setNearClip = SET_PROPERTY_TEMPLATE(nearClip,SNamedPropertyElement::Type::FLOAT,CameraBase); - auto setFarClip = SET_PROPERTY_TEMPLATE(farClip,SNamedPropertyElement::Type::FLOAT,CameraBase); - auto setFocusDistance = SET_PROPERTY_TEMPLATE(focusDistance,SNamedPropertyElement::Type::FLOAT,DepthOfFieldBase); - auto setApertureRadius = SET_PROPERTY_TEMPLATE(apertureRadius,SNamedPropertyElement::Type::FLOAT,DepthOfFieldBase); - //auto setKc = SET_PROPERTY_TEMPLATE(apertureRadius,SNamedPropertyElement::Type::STRING,PerspectivePinholeRadialDistortion); - - const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> SetPropertyMap = - { - //{"focalLength", noIdeaHowToProcessValue}, - {"up", setUp}, - {"clipPlane0", setClipPlane}, - {"clipPlane1", setClipPlane}, - {"clipPlane2", setClipPlane}, - {"clipPlane3", setClipPlane}, - {"clipPlane4", setClipPlane}, - {"clipPlane5", setClipPlane}, - // UPDATE WHENEVER `MaxClipPlanes` changes! - {"shiftX", setShiftX}, - {"shiftY", setShiftY}, - {"fov", setFov}, - {"fovAxis", setFovAxis}, - {"shutterOpen", setShutterOpen}, - {"shuttterClose", setShutterClose}, - {"moveSpeed", setMoveSpeed}, - {"zoomSpeed", setZoomSpeed}, - {"rotateSpeed", setRotateSpeed}, - {"nearClip", setNearClip}, - {"farClip", setFarClip}, - {"focusDistance", setFocusDistance}, - {"apertureRadius", setApertureRadius}/*, - {"kc", setKc}*/ + return false; + } + const auto index = std::atoi(sv.data()+Name.length()); + if (index>MaxClipPlanes) + { + return false; + } + // everyone inherits from this + _this->perspective.clipPlanes[index] = _property.vvalue; + return true; }; - + for (auto i=0; isecond(); - return !error; + return retval; } -bool CElementSensor::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* meta) +bool CElementSensor::onEndTag(CMitsubaMetadata* meta, system::logger_opt_ptr logger) { - if (type == Type::INVALID) - { - ParserLog::invalidXMLFileStructure(getLogName() + ": type not specified"); - _NBL_DEBUG_BREAK_IF(true); - return true; - } + NBL_EXT_MITSUBA_LOADER_ELEMENT_INVALID_TYPE_CHECK(true); // TODO: some validation @@ -257,6 +95,4 @@ bool CElementSensor::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _overri return true; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementShape.cpp b/src/nbl/ext/MitsubaLoader/CElementShape.cpp index 8e6c468821..03b50f3f7c 100644 --- a/src/nbl/ext/MitsubaLoader/CElementShape.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementShape.cpp @@ -1,213 +1,59 @@ // 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/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" +#include "nbl/ext/MitsubaLoader/CElementShape.h" + +#include "nbl/ext/MitsubaLoader/ElementMacros.h" +#include "nbl/type_traits.h" // legacy stuff for `is_any_of` #include -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader +namespace nbl::ext::MitsubaLoader { -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) +auto CElementShape::compAddPropertyMap() -> AddPropertyMap { - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr,""); + using this_t = CElementShape; + AddPropertyMap retval; - static const core::unordered_map StringToType = - { - {"cube", CElementShape::Type::CUBE}, - {"sphere", CElementShape::Type::SPHERE}, - {"cylinder", CElementShape::Type::CYLINDER}, - {"rectangle", CElementShape::Type::RECTANGLE}, - {"disk", CElementShape::Type::DISK}, - {"obj", CElementShape::Type::OBJ}, - {"ply", CElementShape::Type::PLY}, - {"serialized", CElementShape::Type::SERIALIZED}, - {"shapegroup", CElementShape::Type::SHAPEGROUP}, - {"instance", CElementShape::Type::INSTANCE}/*, - {"hair", CElementShape::Type::HAIR}, - {"heightfield", CElementShape::Type::HEIGHTFIELD}*/ - }; - auto found = StringToType.find(type); - if (found==StringToType.end()) - { - ParserLog::invalidXMLFileStructure("unknown type"); - _NBL_DEBUG_BREAK_IF(false); - return CElementFactory::return_type(nullptr, ""); - } + // base + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(flipNormals,BOOLEAN,derived_from,Base); + // cube has nothing - CElementShape* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr, ""); + // sphere + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(center,POINT,std::is_same,Sphere); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(radius,FLOAT,is_any_of,Sphere,Cylinder/*,Hair*/); - obj->type = found->second; - // defaults - switch (obj->type) - { - case CElementShape::Type::CUBE: - obj->cube = CElementShape::Base(); - break; - case CElementShape::Type::SPHERE: - obj->sphere = CElementShape::Sphere(); - break; - case CElementShape::Type::CYLINDER: - obj->cylinder = CElementShape::Cylinder(); - break; - case CElementShape::Type::RECTANGLE: - obj->rectangle = CElementShape::Base(); - break; - case CElementShape::Type::DISK: - obj->disk = CElementShape::Base(); - break; - case CElementShape::Type::OBJ: - obj->obj = CElementShape::Obj(); - break; - case CElementShape::Type::PLY: - obj->ply = CElementShape::Ply(); - break; - case CElementShape::Type::SERIALIZED: - obj->serialized = CElementShape::Serialized(); - break; - case CElementShape::Type::SHAPEGROUP: - obj->shapegroup = CElementShape::ShapeGroup(); - break; - case CElementShape::Type::INSTANCE: - obj->instance = CElementShape::Instance(); - break; - default: - break; - } - return CElementFactory::return_type(obj, std::move(name)); -} + // cylinder + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(p0,POINT,std::is_same,Cylinder); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(p1,POINT,std::is_same,Cylinder); + // COMMON: radius -bool CElementShape::addProperty(SNamedPropertyElement&& _property) -{ - bool error = false; - auto dispatch = [&](auto func) -> void - { - switch (type) + // LoadedFromFileBase + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("filename",STRING,derived_from,LoadedFromFileBase) { - case CElementShape::Type::CUBE: - func(cube); - break; - case CElementShape::Type::SPHERE: - func(sphere); - break; - case CElementShape::Type::CYLINDER: - func(cylinder); - break; - case CElementShape::Type::RECTANGLE: - func(rectangle); - break; - case CElementShape::Type::DISK: - func(disk); - break; - case CElementShape::Type::OBJ: - func(obj); - break; - case CElementShape::Type::PLY: - func(ply); - break; - case CElementShape::Type::SERIALIZED: - func(serialized); - break; - case CElementShape::Type::SHAPEGROUP: - func(shapegroup); - break; - case CElementShape::Type::INSTANCE: - func(instance); - break;/* - case CElementShape::Type::HAIR: - func(hair); - break; - case CElementShape::Type::HEIGHTFIELD: - func(heightfield); - break;*/ - default: - error = true; - break; + setLimitedString("filename",_this->serialized.filename,_property,logger); return true; } - }; - -#define SET_PROPERTY_TEMPLATE(MEMBER,PROPERTY_TYPE, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ - { \ - if (_property.type!=PROPERTY_TYPE) { \ - error = true; \ - return; \ - } \ - state. ## MEMBER = _property.getProperty(); \ - } \ - }); \ - } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(faceNormals,BOOLEAN,derived_from,LoadedFromFileBase); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(maxSmoothAngle,FLOAT,derived_from,LoadedFromFileBase); - auto setFlipNormals = SET_PROPERTY_TEMPLATE(flipNormals,SNamedPropertyElement::Type::BOOLEAN, Base,Sphere,Cylinder,Obj,Ply,Serialized/*,Heightfield*/); - auto setCenter = SET_PROPERTY_TEMPLATE(center,SNamedPropertyElement::Type::POINT, Sphere); - auto setRadius = SET_PROPERTY_TEMPLATE(radius,SNamedPropertyElement::Type::FLOAT, Sphere,Cylinder/*,Hair*/); - auto setP0 = SET_PROPERTY_TEMPLATE(p0,SNamedPropertyElement::Type::POINT, Cylinder); - auto setP1 = SET_PROPERTY_TEMPLATE(p1,SNamedPropertyElement::Type::POINT, Cylinder); - auto setFilename = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; + // Obj + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(flipTexCoords,BOOLEAN,std::is_same,Obj); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(collapse,BOOLEAN,std::is_same,Obj); - if constexpr (is_any_of::value) - { - state.filename = std::move(_property); - } - }); - }; - auto setFaceNormals = SET_PROPERTY_TEMPLATE(faceNormals,SNamedPropertyElement::Type::BOOLEAN, Obj,Ply,Serialized); - auto setMaxSmoothAngle = SET_PROPERTY_TEMPLATE(maxSmoothAngle,SNamedPropertyElement::Type::FLOAT, Obj,Ply,Serialized); - auto setFlipTexCoords = SET_PROPERTY_TEMPLATE(flipTexCoords,SNamedPropertyElement::Type::BOOLEAN, Obj); - auto setCollapse = SET_PROPERTY_TEMPLATE(collapse,SNamedPropertyElement::Type::BOOLEAN, Obj); - auto setSRGB = SET_PROPERTY_TEMPLATE(srgb,SNamedPropertyElement::Type::BOOLEAN, Ply); - auto setShapeIndex = SET_PROPERTY_TEMPLATE(shapeIndex,SNamedPropertyElement::Type::INTEGER, Serialized); - //auto setToWorld = SET_PROPERTY_TEMPLATE(toWorld, SNamedPropertyElement::Type::MATRIX, Instance); + // Ply + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(srgb,BOOLEAN,std::is_same,Ply); - const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> SetPropertyMap = - { - {"flipNormals", setFlipNormals}, - {"center", setCenter}, - {"radius", setRadius}, - {"p0", setP0}, - {"p1", setP1}, - {"filename", setFilename}, - {"faceNormals", setFaceNormals}, - {"maxSmoothAngle", setMaxSmoothAngle}, - {"flipTexCoords", setFlipTexCoords}, - {"collapse", setCollapse}, - {"srgb", setSRGB}, - {"shapeIndex", setShapeIndex}/*, - {"", set}*/ - }; - - - auto found = SetPropertyMap.find(_property.name); - if (found==SetPropertyMap.end()) - { - _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("No Integrator can have such property set with name: "+_property.name); - return false; - } + // Serialized + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(shapeIndex,INTEGER,std::is_same,Serialized); - found->second(); - return !error; + return retval; } -bool CElementShape::processChildData(IElement* _child, const std::string& name) +bool CElementShape::processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) { if (!_child) return true; @@ -217,7 +63,10 @@ bool CElementShape::processChildData(IElement* _child, const std::string& name) { auto tform = static_cast(_child); if (name!="toWorld") + { + logger.log("The nested inside needs to be named \"toWorld\"",system::ILogger::ELL_ERROR); return false; + } //toWorldType = IElement::Type::TRANSFORM; transform = *tform; return true; @@ -237,33 +86,44 @@ bool CElementShape::processChildData(IElement* _child, const std::string& name) switch (type) { case Type::SHAPEGROUP: - if (child->type==Type::INVALID || child->type==Type::SHAPEGROUP) + if (child->type == Type::INVALID || child->type == Type::SHAPEGROUP) + { + logger.log(" cannot be nested inside each other or have INVALID shapes nested inside.",system::ILogger::ELL_ERROR); return false; - if (shapegroup.childCount == ShapeGroup::MaxChildCount) + } + if (shapegroup.childCount==ShapeGroup::MaxChildCount) { - ParserLog::invalidXMLFileStructure("Maximum shape-group children exceeded."); + logger.log("The 's MaxChildCount of %d exceeded!",system::ILogger::ELL_ERROR,ShapeGroup::MaxChildCount); return false; } shapegroup.children[shapegroup.childCount++] = child; return true; - break; case Type::INSTANCE: - if (child->type != Type::SHAPEGROUP) + if (child->type!=Type::SHAPEGROUP) + { + logger.log("Only can be nested inside ",system::ILogger::ELL_ERROR); return false; - instance.parent = child; // yeah I kknow its fucked up, but its the XML child, but Abstract Syntax Tree (or Scene Tree) parent + } + if (instance.parent) + logger.log(" 's parent already set to %s, overriding",system::ILogger::ELL_WARNING,instance.parent->id.c_str()); + instance.parent = child; // yeah I know its messed up, but its the XML child, not the Abstract Syntax Tree (or Scene Tree) parent return true; - break; default: - break; + logger.log("Only and support nesting other s inside",system::ILogger::ELL_ERROR); + return false; } } break; case IElement::Type::BSDF: + if (bsdf) + logger.log("'s BSDF already set to %s, overriding",system::ILogger::ELL_WARNING,bsdf->id.c_str()); bsdf = static_cast(_child); if (bsdf->type != CElementBSDF::Type::INVALID) return true; break; case IElement::Type::EMITTER: + if (emitter) + logger.log("'s Emitter already set to %s, overriding",system::ILogger::ELL_WARNING,emitter->id.c_str()); emitter = static_cast(_child); if (emitter->type != CElementEmitter::Type::INVALID) return true; @@ -271,23 +131,17 @@ bool CElementShape::processChildData(IElement* _child, const std::string& name) default: break; } + logger.log("Invalid or unsupported child with ID %s and Name %s nested inside of ",system::ILogger::ELL_ERROR,_child->id.c_str(),name.c_str()); return false; } -bool CElementShape::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) +bool CElementShape::onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) { - if (type == Type::INVALID) - { - ParserLog::invalidXMLFileStructure(getLogName() + ": type not specified"); - _NBL_DEBUG_BREAK_IF(true); - return true; - } + NBL_EXT_MITSUBA_LOADER_ELEMENT_INVALID_TYPE_CHECK(true); // TODO: some validation return true; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementTexture.cpp b/src/nbl/ext/MitsubaLoader/CElementTexture.cpp index 1ae16b8c07..213a52cba3 100644 --- a/src/nbl/ext/MitsubaLoader/CElementTexture.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementTexture.cpp @@ -1,260 +1,118 @@ -// 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/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" +#include "nbl/ext/MitsubaLoader/CElementTexture.h" -#include +#include "nbl/ext/MitsubaLoader/ElementMacros.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ +#include -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) +namespace nbl::ext::MitsubaLoader { - const char* type; - const char* id; - std::string name; - if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) - return CElementFactory::return_type(nullptr,""); - - static const core::unordered_map StringToType = +inline CElementTexture::Bitmap::WRAP_MODE getWrapMode(const SPropertyElementData& _property) +{ + using mode_e = CElementTexture::Bitmap::WRAP_MODE; + static const core::unordered_map StringToWrap = { - {"bitmap", CElementTexture::Type::BITMAP}, - {"scale", CElementTexture::Type::SCALE} + {"repeat", mode_e::REPEAT}, + {"mirror", mode_e::MIRROR}, + {"clamp", mode_e::CLAMP}, + {"zero", mode_e::ZERO}, + {"one", mode_e::ONE} }; - - auto found = StringToType.find(type); - if (found==StringToType.end()) - { - ParserLog::invalidXMLFileStructure("unknown type"); - _NBL_DEBUG_BREAK_IF(false); - return CElementFactory::return_type(nullptr,""); - } - - CElementTexture* obj = _util->objects.construct(id); - if (!obj) - return CElementFactory::return_type(nullptr,""); - - obj->type = found->second; - // defaults - switch (obj->type) - { - case CElementTexture::Type::BITMAP: - obj->bitmap = CElementTexture::Bitmap(); - break; - case CElementTexture::Type::SCALE: - obj->scale = CElementTexture::Scale(); - break; - default: - break; - } - return CElementFactory::return_type(obj, std::move(name)); + assert(_property.type==SPropertyElementData::Type::STRING); + auto found = StringToWrap.find(_property.getProperty()); + if (found != StringToWrap.end()) + return found->second; + return mode_e::REPEAT; } -bool CElementTexture::addProperty(SNamedPropertyElement&& _property) +auto CElementTexture::compAddPropertyMap() -> AddPropertyMap { - if (type==CElementTexture::Type::SCALE) - { - if (_property.type!=SPropertyElementData::Type::FLOAT) - return false; - scale.scale = _property.fvalue; - return true; - } - + using this_t = CElementTexture; + AddPropertyMap retval; - bool error = false; - auto dispatch = [&](auto func) -> void - { - switch (type) + // bitmap + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("filename",STRING,std::is_same,Bitmap) { - case CElementTexture::Type::BITMAP: - func(bitmap); - break; - case CElementTexture::Type::SCALE: - func(scale); - break; - default: - error = true; - break; + setLimitedString("filename",_this->bitmap.filename,_property,logger); return true; } - }; - -#define SET_PROPERTY_TEMPLATE(MEMBER,PROPERTY_TYPE, ... ) [&]() -> void { \ - dispatch([&](auto& state) -> void { \ - if constexpr (is_any_of::type,__VA_ARGS__>::value) \ - { \ - if (_property.type!=PROPERTY_TYPE) { \ - error = true; \ - return; \ - } \ - state. ## MEMBER = _property.getProperty(); \ - } \ - }); \ - } - - auto processFilename = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) - { - bitmap.filename = std::move(_property); - } - }); - }; - auto getWrapMode = [&]() -> Bitmap::WRAP_MODE { - static const core::unordered_map StringToWrap = + ); + // special + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("wrapMode",STRING,std::is_same,Bitmap) { - {"repeat", Bitmap::WRAP_MODE::REPEAT}, - {"mirror", Bitmap::WRAP_MODE::MIRROR}, - {"clamp", Bitmap::WRAP_MODE::CLAMP}, - {"zero", Bitmap::WRAP_MODE::ZERO}, - {"one", Bitmap::WRAP_MODE::ONE} - }; - auto found = StringToWrap.end(); - if (_property.type == SPropertyElementData::Type::STRING) - found = StringToWrap.find(_property.getProperty()); - if (found != StringToWrap.end()) - return found->second; - return Bitmap::WRAP_MODE::REPEAT; - }; - auto processWrapMode = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) - { - auto value = getWrapMode(); - state.wrapModeU = value; - state.wrapModeV = value; - } - }); - }; - auto processWrapModeU = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) - { - state.wrapModeU = getWrapMode(); - } - }); - }; - auto processWrapModeV = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) - { - state.wrapModeV = getWrapMode(); - } - }); - }; - auto processGamma = SET_PROPERTY_TEMPLATE(gamma,SPropertyElementData::Type::FLOAT,Bitmap); - auto processFilterType = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) + _this->bitmap.wrapModeV = _this->bitmap.wrapModeU = getWrapMode(_property); + return true; + } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("wrapModeU",STRING,std::is_same,Bitmap) + { + _this->bitmap.wrapModeU = getWrapMode(_property); + return true; + } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("wrapModeV",STRING,std::is_same,Bitmap) + { + _this->bitmap.wrapModeV = getWrapMode(_property); + return true; + } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(gamma,FLOAT,std::is_same,Bitmap); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("filterType",STRING,std::is_same,Bitmap) + { + static const core::unordered_map StringToType = { - static const core::unordered_map StringToType = - { - {"ewa", Bitmap::FILTER_TYPE::EWA}, - {"trilinear", Bitmap::FILTER_TYPE::TRILINEAR}, - {"nearest", Bitmap::FILTER_TYPE::NEAREST} - }; - auto found = StringToType.end(); - if (_property.type==SPropertyElementData::Type::STRING) - found = StringToType.find(_property.getProperty()); - if (found==StringToType.end()) - { - error = true; - return; - } - state.filterType = found->second; - } - }); - }; - auto processMaxAnisotropy = SET_PROPERTY_TEMPLATE(maxAnisotropy,SPropertyElementData::Type::FLOAT,Bitmap); - auto processCache = []() -> void {}; // silently drop - auto processUoffset = SET_PROPERTY_TEMPLATE(uoffset,SPropertyElementData::Type::FLOAT,Bitmap); - auto processVoffset = SET_PROPERTY_TEMPLATE(voffset,SPropertyElementData::Type::FLOAT,Bitmap); - auto processUscale = SET_PROPERTY_TEMPLATE(uscale,SPropertyElementData::Type::FLOAT,Bitmap); - auto processVscale = SET_PROPERTY_TEMPLATE(vscale,SPropertyElementData::Type::FLOAT,Bitmap); - auto processChannel = [&]() -> void - { - dispatch([&](auto& state) -> void { - using state_type = std::remove_reference::type; - - if constexpr (std::is_same::value) + {"ewa", Bitmap::FILTER_TYPE::EWA}, + {"trilinear", Bitmap::FILTER_TYPE::TRILINEAR}, + {"nearest", Bitmap::FILTER_TYPE::NEAREST} + }; + auto found = StringToType.find(_property.getProperty()); + if (found==StringToType.end()) + return false; + _this->bitmap.filterType = found->second; + return true; + } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(maxAnisotropy,FLOAT,std::is_same,Bitmap); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("cache",BOOLEAN,std::is_same,Bitmap) + { + return true; // silently drop + } + ); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(uoffset,FLOAT,std::is_same,Bitmap); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(voffset,FLOAT,std::is_same,Bitmap); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(uscale,FLOAT,std::is_same,Bitmap); + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(vscale,FLOAT,std::is_same,Bitmap); + NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED("channel",STRING,std::is_same,Bitmap) + { + static const core::unordered_map StringToType = { - static const core::unordered_map StringToType = - { - {"r", Bitmap::CHANNEL::R}, - {"g", Bitmap::CHANNEL::G}, - {"b", Bitmap::CHANNEL::B}, - {"a", Bitmap::CHANNEL::A}/*, - {"x", Bitmap::CHANNEL::X}, - {"y", Bitmap::CHANNEL::Y}, - {"z", Bitmap::CHANNEL::Z}*/ - }; - auto found = StringToType.end(); - if (_property.type == SPropertyElementData::Type::STRING) - found = StringToType.find(_property.getProperty()); - if (found == StringToType.end()) - { - error = true; - return; - } - state.channel = found->second; - } - }); - }; - + {"r", Bitmap::CHANNEL::R}, + {"g", Bitmap::CHANNEL::G}, + {"b", Bitmap::CHANNEL::B}, + {"a", Bitmap::CHANNEL::A}/*, + {"x", Bitmap::CHANNEL::X}, + {"y", Bitmap::CHANNEL::Y}, + {"z", Bitmap::CHANNEL::Z}*/ + }; + auto found = StringToType.find(_property.getProperty()); + if (found==StringToType.end()) + return false; + _this->bitmap.channel = found->second; + return true; + } + ); - const core::unordered_map, core::CaseInsensitiveHash, core::CaseInsensitiveEquals> SetPropertyMap = - { - {"filename", processFilename}, - {"wrapMode", processWrapMode}, - {"wrapModeU", processWrapModeU}, - {"wrapModeV", processWrapModeV}, - {"gamma", processGamma}, - {"filterType", processFilterType}, - {"maxAnisotropy", processMaxAnisotropy}, - {"cache", processCache}, - {"uoffset", processUoffset}, - {"voffset", processVoffset}, - {"uscale", processUscale}, - {"vscale", processVscale}, - {"channel", processChannel} - }; - - auto found = SetPropertyMap.find(_property.name); - if (found==SetPropertyMap.end()) - { - _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("No BSDF can have such property set with name: "+_property.name); - return false; - } + // scale + NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(scale,FLOAT,std::is_same,Scale); - found->second(); - return !error; + return retval; } -bool CElementTexture::processChildData(IElement* _child, const std::string& name) +bool CElementTexture::processChildData(IElement* _child, const std::string& name, system::logger_opt_ptr logger) { if (!_child) return true; @@ -271,27 +129,21 @@ bool CElementTexture::processChildData(IElement* _child, const std::string& name break; default: _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("No supported texture can have a texture as child element, except for \"scale\""); + logger.log("Only can have nested elements",system::ILogger::ELL_ERROR); return false; - break; } } - break; + return true; default: - return false; break; } - return true; + logger.log(" does not support nested <%s> elements",system::ILogger::ELL_ERROR,type,_child->getLogName()); + return false; } -bool CElementTexture::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _override, CMitsubaMetadata* globalMetadata) +bool CElementTexture::onEndTag(CMitsubaMetadata* globalMetadata, system::logger_opt_ptr logger) { - if (type == Type::INVALID) - { - ParserLog::invalidXMLFileStructure(getLogName() + ": type not specified"); - _NBL_DEBUG_BREAK_IF(true); - return true; - } + NBL_EXT_MITSUBA_LOADER_ELEMENT_INVALID_TYPE_CHECK(true); // TODO: Validation { @@ -300,6 +152,4 @@ bool CElementTexture::onEndTag(asset::IAssetLoader::IAssetLoaderOverride* _overr return true; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CElementTransform.cpp b/src/nbl/ext/MitsubaLoader/CElementTransform.cpp index 7d7f2922f1..3097022a1a 100644 --- a/src/nbl/ext/MitsubaLoader/CElementTransform.cpp +++ b/src/nbl/ext/MitsubaLoader/CElementTransform.cpp @@ -1,56 +1,33 @@ // 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/ext/MitsubaLoader/CElementTransform.h" #include "nbl/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ - -template<> -CElementFactory::return_type CElementFactory::createElement(const char** _atts, ParserManager* _util) +namespace nbl::ext::MitsubaLoader { - if (IElement::invalidAttributeCount(_atts, 2u)) - return CElementFactory::return_type(nullptr,""); - if (core::strcmpi(_atts[0], "name")) - return CElementFactory::return_type(nullptr,""); - - return CElementFactory::return_type(_util->objects.construct(),_atts[1]); -} -bool CElementTransform::addProperty(SNamedPropertyElement&& _property) +auto CElementTransform::compAddPropertyMap() -> AddPropertyMap { - switch (_property.type) + using this_t = CElementTransform; + AddPropertyMap retval; + + auto setMatrix = [](this_t* _this, SNamedPropertyElement&& _property, const system::logger_opt_ptr logger)->bool { - case SNamedPropertyElement::Type::MATRIX: - [[fallthrough]]; - case SNamedPropertyElement::Type::TRANSLATE: - [[fallthrough]]; - case SNamedPropertyElement::Type::ROTATE: - [[fallthrough]]; - case SNamedPropertyElement::Type::SCALE: - [[fallthrough]]; - case SNamedPropertyElement::Type::LOOKAT: - matrix = core::concatenateBFollowedByA(_property.mvalue, matrix); - break; - default: - { - ParserLog::invalidXMLFileStructure("The transform element does not take child property: "+_property.type); - _NBL_DEBUG_BREAK_IF(true); - return false; - } - break; - } + _this->matrix = mul(_property.mvalue,_this->matrix); + return true; + }; + for (const auto& type : { + SNamedPropertyElement::Type::MATRIX, + SNamedPropertyElement::Type::TRANSLATE, + SNamedPropertyElement::Type::ROTATE, + SNamedPropertyElement::Type::SCALE, + SNamedPropertyElement::Type::LOOKAT + }) + retval.registerCallback(type,"",{.func=setMatrix}); - return true; + return retval; } -} -} } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CGLSLMitsubaLoaderBuiltinIncludeGenerator.h b/src/nbl/ext/MitsubaLoader/CGLSLMitsubaLoaderBuiltinIncludeGenerator.h deleted file mode 100644 index d3ba886067..0000000000 --- a/src/nbl/ext/MitsubaLoader/CGLSLMitsubaLoaderBuiltinIncludeGenerator.h +++ /dev/null @@ -1,56 +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 __C_GLSL_MITSUBA_LOADER_BUILTIN_INCLUDE_LOADER_H_INCLUDED__ -#define __C_GLSL_MITSUBA_LOADER_BUILTIN_INCLUDE_LOADER_H_INCLUDED__ - -#include "nbl/asset/utils/IShaderCompiler.h" - - -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ - -class CGLSLMitsubaLoaderBuiltinIncludeGenerator : public IShaderCompiler::IIncludeGenerator -{ - public: - using Base = IShaderCompiler::IIncludeGenerator; - using Base::Base; - - std::string_view getPrefix() const override { return "nbl/builtin/glsl/ext/MitsubaLoader"; }; - - private: - static std::string getMaterialCompilerStuff(const std::string& _path) - { - auto args = parseArgumentsFromPath(_path.substr(_path.rfind(".glsl")+6, _path.npos)); - - const auto str = "#define _NBL_EXT_MITSUBA_LOADER_VT_STORAGE_VIEW_COUNT " + args.front() + "\n"; - - return str + - "#include \"nbl/builtin/glsl/ext/MitsubaLoader/material_compiler_compatibility_impl.glsl\"\n"; - } - - protected: - core::vector> getBuiltinNamesToFunctionMapping() const override - { - core::vector> retval; - - const std::string num = "[0-9]+"; - retval.insert(retval.begin(), - { - std::regex{"glsl/ext/MitsubaLoader/material_compiler_compatibility\\.glsl/"+num}, - &getMaterialCompilerStuff - } - ); - return retval; - } -}; - -}}} - - -#endif \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CMakeLists.txt b/src/nbl/ext/MitsubaLoader/CMakeLists.txt index 6efa07ba23..0ec8ba0685 100644 --- a/src/nbl/ext/MitsubaLoader/CMakeLists.txt +++ b/src/nbl/ext/MitsubaLoader/CMakeLists.txt @@ -4,43 +4,43 @@ set(NBL_EXT_INTERNAL_INCLUDE_DIR "${NBL_ROOT_PATH}/include/nbl/ext/MitsubaLoader set(NBL_EXT_MITSUBA_LOADER_H -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/PropertyElement.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/IElement.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CMitsubaMetadata.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementIntegrator.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementSensor.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementFilm.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementRFilter.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementSampler.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementTransform.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementShape.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementBSDF.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementTexture.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementEmitter.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementFactory.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CMitsubaSerializedMetadata.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/ParserUtil.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/PropertyElement.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/IElement.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CMitsubaMetadata.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementIntegrator.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementSensor.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementFilm.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementRFilter.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementSampler.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementTransform.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementShape.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementBSDF.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementTexture.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementEmitter.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CElementEmissionProfile.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CMitsubaSerializedMetadata.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/ParserUtil.h ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CSerializedLoader.h -# ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CMitsubaLoader.h + ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CMitsubaLoader.h # ${NBL_EXT_INTERNAL_INCLUDE_DIR}/CMitsubaMaterialCompilerFrontend.h ) set(NBL_EXT_MITSUBA_LOADER_SRC -# PropertyElement.cpp -# CElementIntegrator.cpp -# CElementSensor.cpp -# CElementFilm.cpp -# CElementRFilter.cpp -# CElementSampler.cpp -# CElementTransform.cpp -# CElementShape.cpp -# CElementBSDF.cpp -# CElementTexture.cpp -# CElementEmitter.cpp -# CElementFactory.cpp -# ParserUtil.cpp + PropertyElement.cpp + CElementIntegrator.cpp + CElementSensor.cpp + CElementFilm.cpp + CElementRFilter.cpp + CElementSampler.cpp + CElementTransform.cpp + CElementShape.cpp + CElementBSDF.cpp + CElementTexture.cpp + CElementEmitter.cpp + CElementEmissionProfile.cpp + ParserUtil.cpp CSerializedLoader.cpp -# CMitsubaLoader.cpp + CMitsubaLoader.cpp # CMitsubaMaterialCompilerFrontend.cpp ) diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index 093a5b0624..0151bc5578 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -1,19 +1,18 @@ -#include "..\..\..\..\include\nbl\ext\MitsubaLoader\CMitsubaLoader.h" // 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 "os.h" #include #include "nbl/ext/MitsubaLoader/CMitsubaLoader.h" #include "nbl/ext/MitsubaLoader/ParserUtil.h" +#if 0 #include "nbl/asset/utils/CDerivativeMapCreator.h" #include "nbl/ext/MitsubaLoader/CMitsubaSerializedMetadata.h" -#include "nbl/ext/MitsubaLoader/CGLSLMitsubaLoaderBuiltinIncludeLoader.h" +#endif #if defined(_NBL_DEBUG) || defined(_NBL_RELWITHDEBINFO) @@ -24,95 +23,32 @@ namespace nbl { using namespace asset; -namespace ext -{ -namespace MitsubaLoader -{ - -_NBL_STATIC_INLINE_CONSTEXPR const char* DUMMY_VERTEX_SHADER = -R"(#version 430 core - -layout (location = 0) in vec3 vPosition; -layout (location = 2) in vec2 vUV; -layout (location = 3) in vec3 vNormal; - -layout (location = 0) out vec3 WorldPos; -layout (location = 1) flat out uint InstanceIndex; -layout (location = 2) out vec3 Normal; -layout (location = 3) out vec2 UV; - -#include -#include - -#ifndef _NBL_VERT_SET1_BINDINGS_DEFINED_ -#define _NBL_VERT_SET1_BINDINGS_DEFINED_ -layout (set = 1, binding = 0, row_major, std140) uniform UBO { - nbl_glsl_SBasicViewParameters params; -} CamData; -#endif //_NBL_VERT_SET1_BINDINGS_DEFINED_ - -#include - -layout (set = 0, binding = 5, row_major, std430) readonly restrict buffer InstDataBuffer { - nbl_glsl_ext_Mitsuba_Loader_instance_data_t data[]; -} InstData; - -void main() +namespace ext::MitsubaLoader { - mat4x3 tform = InstData.data[gl_InstanceIndex].tform; - mat4 mvp = nbl_glsl_pseudoMul4x4with4x3(CamData.params.MVP, tform); - gl_Position = nbl_glsl_pseudoMul4x4with3x1(mvp, vPosition); - WorldPos = nbl_glsl_pseudoMul3x4with3x1(tform, vPosition); - mat3 normalMat = mat3(InstData.data[gl_InstanceIndex].normalMatrixRow0,InstData.data[gl_InstanceIndex].normalMatrixRow1,InstData.data[gl_InstanceIndex].normalMatrixRow2); - Normal = transpose(normalMat)*normalize(vNormal); - UV = vUV; - InstanceIndex = gl_InstanceIndex; -} - -)"; - -_NBL_STATIC_INLINE_CONSTEXPR const char* FRAGMENT_SHADER_PROLOGUE = -R"(#version 430 core -#extension GL_EXT_shader_integer_mix : require -)"; -_NBL_STATIC_INLINE_CONSTEXPR const char* FRAGMENT_SHADER_INPUT_OUTPUT = -R"( -layout (location = 0) in vec3 WorldPos; -layout (location = 1) flat in uint InstanceIndex; -layout (location = 2) in vec3 Normal; -layout (location = 3) in vec2 UV; -layout (location = 0) out vec4 OutColor; -)"; +#if 0 // old material compiler _NBL_STATIC_INLINE_CONSTEXPR const char* FRAGMENT_SHADER_DEFINITIONS = R"( -#include - -layout (set = 1, binding = 0, row_major, std140) uniform UBO { - nbl_glsl_SBasicViewParameters params; -} CamData; - vec3 nbl_glsl_MC_getNormalizedWorldSpaceV() { - vec3 campos = nbl_glsl_SBasicViewParameters_GetEyePos(CamData.params.NormalMatAndEyePos); + vec3 campos = ....; return normalize(campos - WorldPos); } vec3 nbl_glsl_MC_getNormalizedWorldSpaceN() { return normalize(Normal); } -#ifdef TEX_PREFETCH_STREAM -mat2x3 nbl_glsl_perturbNormal_dPdSomething() {return mat2x3(dFdx(WorldPos),dFdy(WorldPos));} + +mat2x3 nbl_glsl_perturbNormal_dPdSomething() +{ + return mat2x3(dFdx(WorldPos),dFdy(WorldPos)); +} mat2 nbl_glsl_perturbNormal_dUVdSomething() { return mat2(dFdx(UV),dFdy(UV)); } -#endif -#define _NBL_USER_PROVIDED_MATERIAL_COMPILER_GLSL_BACKEND_FUNCTIONS_ )"; _NBL_STATIC_INLINE_CONSTEXPR const char* FRAGMENT_SHADER_IMPL = R"( -#include - #ifndef _NBL_BSDF_COS_EVAL_DEFINED_ #define _NBL_BSDF_COS_EVAL_DEFINED_ // Spectrum can be exchanged to a float for monochrome @@ -166,62 +102,9 @@ void main() } #endif )"; - -_NBL_STATIC_INLINE_CONSTEXPR const char* VERTEX_SHADER_CACHE_KEY = "nbl/builtin/specialized_shader/loaders/mitsuba_xml/default"; - -_NBL_STATIC_INLINE_CONSTEXPR uint32_t PAGE_TAB_TEX_BINDING = 0u; -_NBL_STATIC_INLINE_CONSTEXPR uint32_t PHYS_PAGE_VIEWS_BINDING = 1u; -_NBL_STATIC_INLINE_CONSTEXPR uint32_t PRECOMPUTED_VT_DATA_BINDING = 2u; -_NBL_STATIC_INLINE_CONSTEXPR uint32_t INSTR_BUF_BINDING = 3u; -_NBL_STATIC_INLINE_CONSTEXPR uint32_t BSDF_BUF_BINDING = 4u; -_NBL_STATIC_INLINE_CONSTEXPR uint32_t INSTANCE_DATA_BINDING = 5u; -_NBL_STATIC_INLINE_CONSTEXPR uint32_t PREFETCH_INSTR_BUF_BINDING = 6u; -_NBL_STATIC_INLINE_CONSTEXPR uint32_t EMITTER_DATA_BUF_BINDING = 7u; -_NBL_STATIC_INLINE_CONSTEXPR uint32_t DS0_BINDING_COUNT_WO_VT = 6u; - -template -static void insertAssetIntoCache(core::smart_refctd_ptr& asset, const char* path, IAssetManager* _assetMgr) // TODO: @Crisspl this is duplicate code -{ - asset::SAssetBundle bundle(nullptr,{ asset }); - _assetMgr->changeAssetKey(bundle, path); - _assetMgr->insertAssetIntoCache(bundle); -} -// @Crisspl TODO this needs to use the IAssetLoaderOverride instead -template -static auto getBuiltinAsset(const char* _key, IAssetManager* _assetMgr) -> std::enable_if_t, core::smart_refctd_ptr> -{ - size_t storageSz = 1ull; - asset::SAssetBundle bundle; - const IAsset::E_TYPE types[]{ assetType, static_cast(0u) }; - - _assetMgr->findAssets(storageSz, &bundle, _key, types); - auto assets = bundle.getContents(); - if (assets.empty()) - return nullptr; - //assert(!assets.empty()); - - return core::smart_refctd_ptr_static_cast(assets.begin()[0]); -} - -static core::smart_refctd_ptr createSpecShader(const char* _glsl, asset::ISpecializedShader::E_SHADER_STAGE _stage) -{ - auto shader = core::make_smart_refctd_ptr(_glsl); - asset::ICPUSpecializedShader::SInfo info(nullptr, nullptr, "main", _stage); - auto specd = core::make_smart_refctd_ptr(std::move(shader), std::move(info)); - - return specd; -} -static core::smart_refctd_ptr createAndCacheVertexShader(asset::IAssetManager* _manager, const char* _glsl) -{ - auto vs = createSpecShader(_glsl, asset::ISpecializedShader::ESS_VERTEX); - - insertAssetIntoCache(vs, VERTEX_SHADER_CACHE_KEY, _manager); - - return vs; -} static core::smart_refctd_ptr createFragmentShader(const asset::material_compiler::CMaterialCompilerGLSLBackendCommon::result_t& _mcRes, size_t _VTstorageViewCount) { - std::string source = + std::string source = FRAGMENT_SHADER_PROLOGUE + _mcRes.fragmentShaderSource_declarations + FRAGMENT_SHADER_INPUT_OUTPUT + @@ -232,45 +115,7 @@ static core::smart_refctd_ptr createFragmentShader return createSpecShader(source.c_str(), asset::ISpecializedShader::ESS_FRAGMENT); } -static core::smart_refctd_ptr createPipeline(core::smart_refctd_ptr&& _layout, core::smart_refctd_ptr&& _vertshader, core::smart_refctd_ptr&& _fragshader) -{ - auto vs = std::move(_vertshader); - auto fs = std::move(_fragshader); - asset::ICPUSpecializedShader* shaders[2]{ vs.get(), fs.get() }; - - SRasterizationParams rasterParams; - rasterParams.faceCullingMode = asset::EFCM_NONE; - rasterParams.frontFaceIsCCW = 1; - auto pipeline = core::make_smart_refctd_ptr( - std::move(_layout), - shaders, shaders+2, - //all the params will be overriden with those loaded with meshes - SVertexInputParams(), - SBlendParams(), - SPrimitiveAssemblyParams(), - rasterParams - ); - - return pipeline; -} - -static core::smart_refctd_ptr createImageView(core::smart_refctd_ptr&& _img) // TODO: this should seriously be a utility somewhere -{ - const auto& iparams = _img->getCreationParameters(); - - asset::ICPUImageView::SCreationParams params; - params.format = iparams.format; - params.subresourceRange.baseArrayLayer = 0u; - params.subresourceRange.layerCount = iparams.arrayLayers; - assert(params.subresourceRange.layerCount == 1u); - params.subresourceRange.baseMipLevel = 0u; - params.subresourceRange.levelCount = iparams.mipLevels; - params.viewType = asset::IImageView::ET_2D; - params.flags = static_cast::E_CREATE_FLAGS>(0); - params.image = std::move(_img); - - return asset::ICPUImageView::create(std::move(params)); -} +// TODO: move to IAssetLoader static core::smart_refctd_ptr createDerivMap(SContext& ctx, asset::ICPUImage* _heightMap, const ICPUSampler::SParams& _samplerParams, bool fromNormalMap) { core::smart_refctd_ptr derivmap_img; @@ -297,259 +142,176 @@ static core::smart_refctd_ptr createDerivMap(SContext& ctx, as } static core::smart_refctd_ptr createSingleChannelImage(const asset::ICPUImage* _img, const asset::ICPUImageView::SComponentMapping::E_SWIZZLE srcChannel) { - auto outParams = _img->getCreationParameters(); - const auto inFormat = outParams.format; - - asset::ICPUImage::SBufferCopy region; - // pick format - { - // TODO: redo the format selection when @Erfan's format promotor is operational - if (isSRGBFormat(inFormat)) - outParams.format = asset::EF_B8G8R8A8_SRGB; - else - { - const double prec = asset::getFormatPrecision(inFormat,srcChannel,0.0); - if (prec<=FLT_MIN) - outParams.format = asset::EF_R32G32B32A32_SFLOAT; - else if (prec<=1.0/65535.0) - outParams.format = asset::EF_R16G16B16A16_UNORM; - else if (prec<=exp2f(-14.f)) - outParams.format = asset::EF_R16G16B16A16_SFLOAT; - else if (prec<=1.0/1023.0) - outParams.format = asset::EF_A2B10G10R10_UNORM_PACK32; - else - outParams.format = asset::EF_R8G8B8A8_UNORM; - } - } - const size_t texelBytesz = asset::getTexelOrBlockBytesize(outParams.format); - region.bufferRowLength = asset::IImageAssetHandlerBase::calcPitchInBlocks(outParams.extent.width, texelBytesz); - auto buffer = core::make_smart_refctd_ptr(texelBytesz * region.bufferRowLength * outParams.extent.height); - region.imageOffset = { 0,0,0 }; - region.imageExtent = outParams.extent; - region.imageSubresource.baseArrayLayer = 0u; - region.imageSubresource.layerCount = 1u; - region.imageSubresource.mipLevel = 0u; - region.bufferImageHeight = 0u; - region.bufferOffset = 0u; - auto outImg = asset::ICPUImage::create(std::move(outParams)); - outImg->setBufferAndRegions(std::move(buffer), core::make_refctd_dynamic_array>(1ull, region)); - - using convert_filter_t = asset::CSwizzleAndConvertImageFilter; - convert_filter_t::state_type conv; - conv.extent = outParams.extent; - conv.layerCount = 1u; - conv.inMipLevel = 0u; - conv.outMipLevel = 0u; - conv.inBaseLayer = 0u; - conv.outBaseLayer = 0u; - conv.inOffset = { 0u,0u,0u }; - conv.outOffset = { 0u,0u,0u }; - conv.inImage = _img; - conv.outImage = outImg.get(); - if (srcChannel!=asset::ICPUImageView::SComponentMapping::E_SWIZZLE::ES_IDENTITY) - conv.swizzle = {srcChannel,srcChannel,srcChannel,srcChannel}; - else - { - conv.swizzle = {}; - for (auto i=asset::getFormatChannelCount(inFormat); i<4; i++) - conv.swizzle[i] = asset::ICPUImageView::SComponentMapping::E_SWIZZLE::ES_R; - } - - if (!convert_filter_t::execute(std::execution::par_unseq,&conv)) - { - os::Printer::log("Mitsuba XML Loader: blend weight texture creation failed!", ELL_ERROR); - _NBL_DEBUG_BREAK_IF(true); - } - - return outImg; -} - -core::smart_refctd_ptr CMitsubaLoader::createPipelineLayout(asset::IAssetManager* _manager, const asset::ICPUVirtualTexture* _vt) -{ - core::smart_refctd_ptr ds0layout; - { - auto sizes = _vt->getDSlayoutBindings(nullptr, nullptr); - auto bindings = core::make_refctd_dynamic_array>(sizes.first + DS0_BINDING_COUNT_WO_VT); - auto samplers = core::make_refctd_dynamic_array< core::smart_refctd_dynamic_array>>(sizes.second); - - _vt->getDSlayoutBindings(bindings->data(), samplers->data(), PAGE_TAB_TEX_BINDING, PHYS_PAGE_VIEWS_BINDING); - auto* b = bindings->data() + (bindings->size() - DS0_BINDING_COUNT_WO_VT); - b[0].binding = PRECOMPUTED_VT_DATA_BINDING; - b[0].count = 1u; - b[0].samplers = nullptr; - b[0].stageFlags = asset::ISpecializedShader::ESS_FRAGMENT; - b[0].type = asset::EDT_STORAGE_BUFFER; - - b[1].binding = INSTR_BUF_BINDING; - b[1].count = 1u; - b[1].samplers = nullptr; - b[1].stageFlags = asset::ISpecializedShader::ESS_FRAGMENT; - b[1].type = asset::EDT_STORAGE_BUFFER; - - b[2].binding = BSDF_BUF_BINDING; - b[2].count = 1u; - b[2].samplers = nullptr; - b[2].stageFlags = asset::ISpecializedShader::ESS_FRAGMENT; - b[2].type = asset::EDT_STORAGE_BUFFER; - - b[3].binding = INSTANCE_DATA_BINDING; - b[3].count = 1u; - b[3].samplers = nullptr; - b[3].stageFlags = static_cast(asset::ISpecializedShader::ESS_FRAGMENT | asset::ISpecializedShader::ESS_VERTEX); - b[3].type = asset::EDT_STORAGE_BUFFER; - - b[4].binding = PREFETCH_INSTR_BUF_BINDING; - b[4].count = 1u; - b[4].samplers = nullptr; - b[4].stageFlags = asset::ISpecializedShader::ESS_FRAGMENT; - b[4].type = asset::EDT_STORAGE_BUFFER; - - b[5].binding = EMITTER_DATA_BUF_BINDING; - b[5].count = 1u; - b[5].samplers = nullptr; - b[5].stageFlags = asset::ISpecializedShader::ESS_FRAGMENT; - b[5].type = asset::EDT_STORAGE_BUFFER; - - ds0layout = core::make_smart_refctd_ptr(bindings->data(), bindings->data() + bindings->size()); - } - auto ds1layout = getBuiltinAsset("nbl/builtin/descriptor_set_layout/basic_view_parameters", _manager); - - return core::make_smart_refctd_ptr(nullptr, nullptr, std::move(ds0layout), std::move(ds1layout), nullptr, nullptr); + // deprecated will be expressed in Material Compiler Frontend AST as a swizzle } - -CMitsubaLoader::CMitsubaLoader(asset::IAssetManager* _manager, io::IFileSystem* _fs) : asset::IRenderpassIndependentPipelineLoader(_manager), m_filesystem(_fs) -{ -#ifdef _NBL_DEBUG - setDebugName("CMitsubaLoader"); #endif -} - -void CMitsubaLoader::initialize() -{ - IRenderpassIndependentPipelineLoader::initialize(); - - auto* glslc = m_assetMgr->getGLSLCompiler(); - - glslc->getIncludeHandler()->addBuiltinIncludeLoader(core::make_smart_refctd_ptr(m_filesystem)); -} +constexpr auto LoggerError = system::ILogger::ELL_ERROR; -bool CMitsubaLoader::isALoadableFileFormat(io::IReadFile* _file) const +bool CMitsubaLoader::isALoadableFileFormat(system::IFile* _file, const system::logger_opt_ptr logger) const { - constexpr uint32_t stackSize = 16u*1024u; + constexpr uint32_t stackSize = 16u<<10u; char tempBuff[stackSize+1]; tempBuff[stackSize] = 0; static const char* stringsToFind[] = { " 2u*maxStringSize, "WTF?"); + static_assert(stackSize>2u*maxStringSize); - const size_t prevPos = _file->getPos(); const auto fileSize = _file->getSize(); - if (fileSize < maxStringSize) + if (fileSizeseek(0); - _file->read(tempBuff, 3u); + size_t pos = 0; bool utf16 = false; - if (tempBuff[0]==0xEFu && tempBuff[1]==0xBBu && tempBuff[2]==0xBFu) - utf16 = false; - else if (reinterpret_cast(tempBuff)[0]==0xFEFFu) { - utf16 = true; - _file->seek(2); + system::IFile::success_t success; + _file->read(success,tempBuff,pos,3); + if (!success) + return false; + if (tempBuff[0] == 0xEFu && tempBuff[1] == 0xBBu && tempBuff[2] == 0xBFu) + utf16 = false; + else if (reinterpret_cast(tempBuff)[0] == 0xFEFFu) + { + utf16 = true; + pos = 2; + } + else + pos = 0; } - else - _file->seek(0); - while (true) + + while (posgetPos(); - if (pos >= fileSize) - break; - if (pos > maxStringSize) - _file->seek(_file->getPos()-maxStringSize); - _file->read(tempBuff,stackSize); + if (pos>maxStringSize) + pos -= maxStringSize; + system::ISystem::future_t bytesRead; + _file->read(bytesRead,tempBuff,pos,stackSize); + if (!bytesRead.wait()) + return false; + tempBuff[bytesRead.copy()] = '\0'; + // TODO: should we require all 3 are found? for (auto i=0u; i(tempBuff),stringsToFindW[i])!=nullptr):(strstr(tempBuff, stringsToFind[i])!=nullptr)) - { - _file->seek(prevPos); + if (utf16 ? (wcsstr(reinterpret_cast(tempBuff),stringsToFindW[i])!=nullptr):(strstr(tempBuff,stringsToFind[i])!=nullptr)) return true; - } } - _file->seek(prevPos); return false; } -const char** CMitsubaLoader::getAssociatedFileExtensions() const +SAssetBundle CMitsubaLoader::loadAsset(system::IFile* _file, const IAssetLoader::SAssetLoadParams& _params, IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel) { - static const char* ext[]{ "xml", nullptr }; - return ext; -} - -asset::SAssetBundle CMitsubaLoader::loadAsset(io::IReadFile* _file, const asset::IAssetLoader::SAssetLoadParams& _params, asset::IAssetLoader::IAssetLoaderOverride* _override, uint32_t _hierarchyLevel) -{ - ParserManager parserManager(m_assetMgr->getFileSystem(),_override); - if (!parserManager.parse(_file)) + auto result = m_parser.parse(_file,{.logger=_params.logger,.system=m_system.get(),._override=_override}); + if (!result) return {}; - if (_params.loaderFlags & IAssetLoader::ELPF_LOAD_METADATA_ONLY) + if (_params.loaderFlags&IAssetLoader::ELPF_LOAD_METADATA_ONLY) { - auto emptyMesh = core::make_smart_refctd_ptr(); - return SAssetBundle(std::move(parserManager.m_metadata),{ std::move(emptyMesh) }); + return SAssetBundle(std::move(result.metadata),{ICPUScene::create(nullptr)}); } else { - // - auto currentDir = io::IFileSystem::getFileDir(_file->getFileName()) + "/"; SContext ctx( - m_assetMgr->getGeometryCreator(), - m_assetMgr->getMeshManipulator(), - asset::IAssetLoader::SAssetLoadContext{ - asset::IAssetLoader::SAssetLoadParams(_params.decryptionKeyLen, _params.decryptionKey, _params.cacheFlags, currentDir.c_str()), +// m_assetMgr->getGeometryCreator(), +// m_assetMgr->getMeshManipulator(), + IAssetLoader::SAssetLoadContext{ + IAssetLoader::SAssetLoadParams(_params.decryptionKeyLen,_params.decryptionKey,_params.cacheFlags,_params.loaderFlags,_params.logger,_file->getFileName().parent_path()), _file }, _override, - parserManager.m_metadata.get() + result.metadata.get() ); - if (!getBuiltinAsset(VERTEX_SHADER_CACHE_KEY, m_assetMgr)) - { - createAndCacheVertexShader(m_assetMgr, DUMMY_VERTEX_SHADER); - } + // + ctx.scene->m_ambientLight = result.ambient; - core::map,std::pair> meshes; - for (auto& shapepair : parserManager.shapegroups) - { - auto* shapedef = shapepair.first; - if (shapedef->type == CElementShape::Type::SHAPEGROUP) - continue; - auto lowermeshes = getMesh(ctx, _hierarchyLevel, shapedef); - for (auto& mesh : lowermeshes) + // TODO: abstract/move away since many loaders will need to do this + core::unordered_map> morphTargetCache; + auto createMorphTargets = [&_params,&morphTargetCache](core::smart_refctd_ptr&& collection)->core::smart_refctd_ptr + { + auto found = morphTargetCache.find(collection.get()); + if (found!=morphTargetCache.end()) + return found->second; + auto targets = core::make_smart_refctd_ptr(); + if (targets) { - if (!mesh) - continue; + morphTargetCache[collection.get()] = targets; + targets->getTargets()->push_back({.geoCollection=std::move(collection)}); + } + return targets; + }; - auto found = meshes.find(mesh); - if (found == meshes.end()) - meshes.emplace(std::move(mesh),std::pair(std::move(shapepair.second),shapedef->type)); + // + auto& instances = ctx.scene->getInstances(); + instances.reserve(result.shapegroups.size()); + auto addToScene = [&](const CElementShape* shape, core::smart_refctd_ptr&& collection)->void + { + assert(shape && collection); + auto targets = createMorphTargets(std::move(collection)); + if (!targets) + { + _params.logger.log("Failed to create ICPUMorphTargets for Shape with id %s",LoggerError,shape->id.c_str()); + return; } - } + const auto index = instances.size(); + instances.resize(index+1); + instances.getMorphTargets()[index] = std::move(targets); + // TODO: add materials (incl emission) to the instances + /* + auto emitter = shape->obtainEmitter(); + auto bsdf = getBSDFtreeTraversal(ctx, shape->bsdf, &emitter, getAbsoluteTransform()); + + SContext::SInstanceData instance( + tform, + bsdf, + #if defined(_NBL_DEBUG) || defined(_NBL_RELWITHDEBINFO) + shape->bsdf ? shape->bsdf->id : "", + #endif + emitter, + CElementEmitter{} // no backface emission + ); + */ + instances.getInitialTransforms()[index] = shape->getTransform(); + }; - parserManager.m_metadata->reserveMeshStorage(meshes.size(),ctx.mapMesh2instanceData.size()); - for (auto& mesh : meshes) + // first go over all actually used shapes which are not shapegroups (regular shapes and instances) + for (auto& shapepair : result.shapegroups) { - auto instances_rng = ctx.mapMesh2instanceData.equal_range(mesh.first.get()); - assert(instances_rng.first!=instances_rng.second); + auto* shapedef = shapepair.element; + // this should be filtered out while parsing and we should just assert it + if (shapedef->type==CElementShape::Type::SHAPEGROUP) + continue; - const uint32_t instanceCount = parserManager.m_metadata->addMeshMeta(mesh.first.get(),std::move(mesh.second.first),mesh.second.second,instances_rng.first,instances_rng.second); - for (auto mb : mesh.first.get()->getMeshBuffers()) - mb->setInstanceCount(instanceCount); + if (shapedef->type!=CElementShape::Type::INSTANCE) + { + auto geometry = ctx.loadBasicShape(_hierarchyLevel,shapedef); + if (!geometry) + continue; + auto collection = core::make_smart_refctd_ptr(); + if (!collection) + { + _params.logger.log("Failed to create an ICPUGeometryCollection non-Instanced Shape with id %s",LoggerError,shapedef->id.c_str()); + continue; + } + // we don't put a transform on the geometry, because we want the transform on the instance + collection->getGeometries()->push_back({.geometry=std::move(geometry)}); + addToScene(shapedef,std::move(collection)); + } + else // mitsuba is weird and lists instances under a shapegroup instead of having instances reference the shapegroup + { + // get group reference + const CElementShape* parent = shapedef->instance.parent; + if (!parent) // we should probably assert this + continue; + assert(parent->type==CElementShape::Type::SHAPEGROUP); + auto collection = ctx.loadShapeGroup(_hierarchyLevel,&parent->shapegroup); + addToScene(shapedef,std::move(collection)); + } } + result.shapegroups.clear(); +#if 0 // TODO: put IR and stuff in metadata so that we can recompile the materials after load auto compResult = ctx.backend.compile(&ctx.backend_ctx, ctx.ir.get(), decltype(ctx.backend)::EGST_PRESENT_WITH_AOV_EXTRACTION); ctx.backend_ctx.vt.commitAll(); - auto pipelineLayout = createPipelineLayout(m_assetMgr, ctx.backend_ctx.vt.getCPUVirtualTexture()); auto fragShader = createFragmentShader(compResult, ctx.backend_ctx.vt.getCPUVirtualTexture()->getFloatViews().size()); auto ds0 = createDS0(ctx, pipelineLayout.get(), compResult, meshes.begin(), meshes.end()); auto basePipeline = createPipeline( @@ -566,188 +328,420 @@ asset::SAssetBundle CMitsubaLoader::loadAsset(io::IReadFile* _file, const asset: { ctx.meta->addDerivMapMeta(derivMap.first.get(), derivMap.second); } - - auto meshSmartPtrArray = core::make_refctd_dynamic_array(meshes.size()); - auto meshSmartPtrArrayIt = meshSmartPtrArray->begin(); - for (const auto& mesh_ : meshes) - { - for (auto mb : mesh_.first.get()->getMeshBuffers()) - { - const auto* prevPipeline = mb->getPipeline(); - SContext::SPipelineCacheKey cacheKey; - cacheKey.vtxParams = prevPipeline->getVertexInputParams(); - cacheKey.primParams = prevPipeline->getPrimitiveAssemblyParams(); - auto found = ctx.pipelineCache.find(cacheKey); - core::smart_refctd_ptr pipeline; - if (found != ctx.pipelineCache.end()) - { - pipeline = found->second; - } - else - { - pipeline = core::smart_refctd_ptr_static_cast(//shallow copy because we're only going to override parameter structs - basePipeline->clone(0u) - ); - pipeline->getVertexInputParams() = cacheKey.vtxParams; - pipeline->getPrimitiveAssemblyParams() = cacheKey.primParams; - ctx.pipelineCache.insert({ cacheKey, pipeline }); - } - - mb->setPipeline(core::smart_refctd_ptr(pipeline)); - } - *(meshSmartPtrArrayIt++) = std::move(mesh_.first); - } - - parserManager.m_metadata->reservePplnStorage(ctx.pipelineCache.size(),core::smart_refctd_ptr(IRenderpassIndependentPipelineLoader::m_basicViewParamsSemantics)); - for (auto& ppln : ctx.pipelineCache) - parserManager.m_metadata->addPplnMeta(ppln.second.get(),core::smart_refctd_ptr(ds0)); - - for (const auto& emitter : parserManager.m_metadata->m_global.m_emitters) +#endif + for (const auto& emitter : result.emitters) { - if(emitter.type == ext::MitsubaLoader::CElementEmitter::Type::ENVMAP) + if(emitter.element->type == ext::MitsubaLoader::CElementEmitter::Type::ENVMAP) { - assert(emitter.envmap.filename.type==ext::MitsubaLoader::SPropertyElementData::Type::STRING); - auto envfilename = emitter.envmap.filename.svalue; - SAssetBundle envmapImageBundle = interm_getAssetInHierarchy(m_assetMgr, envfilename, ctx.inner.params, _hierarchyLevel, ctx.override_); - auto contentRange = envmapImageBundle.getContents(); + const auto& envmap = emitter.element->envmap; + SAssetBundle envmapBundle = interm_getImageViewInHierarchy(envmap.filename,ctx.inner.params,_hierarchyLevel,ctx.override_); + auto contentRange = envmapBundle.getContents(); if (contentRange.empty()) { - os::Printer::log(std::string("[ERROR] Could Not Find Envmap Image: ") + envfilename, ELL_ERROR); + _params.logger.log("Could not load Envnmap image from path: %s",LoggerError,envmap.filename); continue; } - if (envmapImageBundle.getAssetType()!=asset::IAsset::ET_IMAGE) - { - os::Printer::log("[ERROR] Loaded an Asset for the Envmap but it wasn't an image, was E_ASSET_TYPE " + std::to_string(envmapImageBundle.getAssetType()), ELL_ERROR); - continue; - } - parserManager.m_metadata->m_global.m_envMapImages.push_back(core::smart_refctd_ptr_static_cast(*contentRange.begin())); + ctx.scene->addEnvLight(ICPUScene::EEnvLightType::SphereMap,core::smart_refctd_ptr_static_cast(contentRange[0])); } } - return asset::SAssetBundle(std::move(parserManager.m_metadata),std::move(meshSmartPtrArray)); + ctx.transferMetadata(); + return asset::SAssetBundle(std::move(result.metadata),{std::move(ctx.scene)}); } } -core::vector CMitsubaLoader::getMesh(SContext& ctx, uint32_t hierarchyLevel, CElementShape* shape) +#if 0 +void CMitsubaLoader::cacheEmissionProfile(SContext& ctx, const CElementEmissionProfile* profile) { - if (!shape) - return {}; + if (!profile) + return; - if (shape->type!=CElementShape::Type::INSTANCE) - return {loadBasicShape(ctx, hierarchyLevel, shape, core::matrix3x4SIMD())}; - else + auto params = ctx.inner.params; + params.loaderFlags = asset::IAssetLoader::ELPF_LOAD_METADATA_ONLY; + + auto assetLoaded = interm_getAssetInHierarchy( profile->filename, params, 0u, ctx.override_); + + if (!assetLoaded.getMetadata()) { - core::matrix3x4SIMD relTform = shape->getAbsoluteTransform(); - // get group reference - const CElementShape* parent = shape->instance.parent; - if (!parent) - return {}; - assert(parent->type==CElementShape::Type::SHAPEGROUP); - const CElementShape::ShapeGroup* shapegroup = &parent->shapegroup; - - return loadShapeGroup(ctx, hierarchyLevel, shapegroup, relTform); + os::Printer::log("[ERROR] Could Not Find Emission Profile: " + profile->filename, ELL_ERROR); + return; } } -core::vector CMitsubaLoader::loadShapeGroup(SContext& ctx, uint32_t hierarchyLevel, const CElementShape::ShapeGroup* shapegroup, const core::matrix3x4SIMD& relTform) +void CMitsubaLoader::cacheTexture(SContext& ctx, uint32_t hierarchyLevel, const CElementTexture* tex, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic) { - // @Crisspl why no group cache? - // find group - //auto found = ctx.groupCache.find(shapegroup); - //if (found != ctx.groupCache.end()) - // return found->second; - - const auto children = shapegroup->children; + if (!tex) + return; - core::vector meshes; - for (auto i=0u; ichildCount; i++) + switch (tex->type) { - auto child = children[i]; - if (!child) - continue; - - assert(child->type!=CElementShape::Type::INSTANCE); - if (child->type != CElementShape::Type::SHAPEGROUP) { - auto lowermesh = loadBasicShape(ctx, hierarchyLevel, child, relTform); - meshes.push_back(std::move(lowermesh)); - } - else { - auto lowermeshes = loadShapeGroup(ctx, hierarchyLevel, &child->shapegroup, relTform); - meshes.insert(meshes.begin(), std::make_move_iterator(lowermeshes.begin()), std::make_move_iterator(lowermeshes.end())); - } + case CElementTexture::Type::BITMAP: + { + // get sampler parameters + const auto samplerParams = ctx.computeSamplerParameters(tex->bitmap); + + asset::SAssetBundle viewBundle = interm_getImageViewInHierarchy(tex->bitmap.filename.svalue,ctx.inner,hierarchyLevel,ctx.override_); + // TODO: embed the gamma in the material compiler Frontend + // adjust gamma on pixels (painful and long process) + if (!std::isnan(tex->bitmap.gamma)) + { + _NBL_DEBUG_BREAK_IF(true); + } + { + //! TODO: this stuff (custom shader sampling code?) + _NBL_DEBUG_BREAK_IF(tex->bitmap.uoffset != 0.f); + _NBL_DEBUG_BREAK_IF(tex->bitmap.voffset != 0.f); + _NBL_DEBUG_BREAK_IF(tex->bitmap.uscale != 1.f); + _NBL_DEBUG_BREAK_IF(tex->bitmap.vscale != 1.f); + } + } + break; + case CElementTexture::Type::SCALE: + // get to to the linked list end + cacheTexture(ctx,hierarchyLevel,tex->scale.texture,semantic); + break; + default: + _NBL_DEBUG_BREAK_IF(true); + break; } - - //ctx.groupCache.insert({shapegroup,meshes}); - return meshes; } -static core::smart_refctd_ptr createMeshFromGeomCreatorReturnType(IGeometryCreator::return_type&& _data, asset::IAssetManager* _manager) +auto CMitsubaLoader::getBSDFtreeTraversal(SContext& ctx, const CElementBSDF* bsdf, const CElementEmitter* emitter, core::matrix4SIMD tform) -> SContext::bsdf_type { - //creating pipeline just to forward vtx and primitive params - auto pipeline = core::make_smart_refctd_ptr( - nullptr, nullptr, nullptr, //no layout nor shaders - _data.inputParams, - asset::SBlendParams(), - _data.assemblyParams, - asset::SRasterizationParams() - ); + if (!bsdf) + { + static auto blackBSDF = []() -> auto + { + CElementBSDF retval("nullptr BSDF"); + retval.type = CElementBSDF::Type::DIFFUSE, + retval.diffuse.reflectance = 0.f; + retval.diffuse.alpha = 0.f; + return retval; + }(); + bsdf = &blackBSDF; + } - auto mb = core::make_smart_refctd_ptr( - nullptr, nullptr, - _data.bindings, std::move(_data.indexBuffer) - ); - mb->setIndexCount(_data.indexCount); - mb->setIndexType(_data.indexType); - mb->setBoundingBox(_data.bbox); - mb->setPipeline(std::move(pipeline)); - constexpr auto NORMAL_ATTRIBUTE = 3; - mb->setNormalAttributeIx(NORMAL_ATTRIBUTE); + auto found = ctx.instrStreamCache.find(bsdf); + if (found == ctx.instrStreamCache.end()) + found = ctx.instrStreamCache.insert({ bsdf,genBSDFtreeTraversal(ctx, bsdf) }).first; + auto compiled_bsdf = found->second; - auto mesh = core::make_smart_refctd_ptr(); - mesh->getMeshBufferVector().push_back(std::move(mb)); + // TODO cache the IR Node + CMitsubaMaterialCompilerFrontend::EmitterNode* emitterIRNode = nullptr; + if (emitter->type == CElementEmitter::AREA) + { + cacheEmissionProfile(ctx,emitter->area.emissionProfile); + emitterIRNode = ctx.frontend.createEmitterNode(ctx.ir.get(),emitter,tform); + } - return mesh; + // A new root node gets made for every {bsdf,emitter} combo + using node_t = asset::material_compiler::IR::INode; + auto createNewRootNode = [&ctx,emitterIRNode](node_t* realBxDFRoot, node_t* emitter=nullptr) -> node_t* + { + // TODO: cache the combo! + auto newRoot = ctx.ir->allocNode(); + if (emitter) + newRoot->children = node_t::createChildrenArray(realBxDFRoot,emitter); + else + newRoot->children = node_t::createChildrenArray(realBxDFRoot); + ctx.ir->addRootNode(newRoot); + return newRoot; + }; + + return { createNewRootNode(compiled_bsdf.front,emitterIRNode), createNewRootNode(compiled_bsdf.back)}; } -SContext::shape_ass_type CMitsubaLoader::loadBasicShape(SContext& ctx, uint32_t hierarchyLevel, CElementShape* shape, const core::matrix3x4SIMD& relTform) +auto CMitsubaLoader::genBSDFtreeTraversal(SContext& ctx, const CElementBSDF* _bsdf) -> SContext::bsdf_type { - constexpr uint32_t UV_ATTRIB_ID = 2u; - - auto addInstance = [shape,&ctx,&relTform,this](SContext::shape_ass_type& mesh) { - auto emitter = shape->obtainEmitter(); - core::matrix3x4SIMD tform = core::concatenateBFollowedByA(relTform, shape->getAbsoluteTransform()); - auto bsdf = getBSDFtreeTraversal(ctx, shape->bsdf, &emitter, core::matrix4SIMD(tform)); - - SContext::SInstanceData instance( - tform, - bsdf, -#if defined(_NBL_DEBUG) || defined(_NBL_RELWITHDEBINFO) - shape->bsdf ? shape->bsdf->id : "", -#endif - emitter, - CElementEmitter{} - ); - ctx.mapMesh2instanceData.insert({ mesh.get(), instance }); - }; - - auto found = ctx.shapeCache.find(shape); - if (found != ctx.shapeCache.end()) { - addInstance(found->second); + auto cachePropertyTexture = [&](const auto& const_or_tex, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic=CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY) -> void + { + if (const_or_tex.value.type==SPropertyElementData::INVALID) + cacheTexture(ctx,0u,const_or_tex.texture,semantic); + }; - return found->second; - } + core::stack stack; + stack.push(_bsdf); - auto loadModel = [&](const ext::MitsubaLoader::SPropertyElementData& filename, int64_t index=-1) -> core::smart_refctd_ptr - { - assert(filename.type==ext::MitsubaLoader::SPropertyElementData::Type::STRING); - auto loadParams = ctx.inner.params; - loadParams.loaderFlags = static_cast(loadParams.loaderFlags | IAssetLoader::ELPF_RIGHT_HANDED_MESHES); - auto retval = interm_getAssetInHierarchy(m_assetMgr, filename.svalue, loadParams, hierarchyLevel/*+ICPUScene::MESH_HIERARCHY_LEVELS_BELOW*/, ctx.override_); - if (retval.getContents().empty()) + while (!stack.empty()) { - os::Printer::log(std::string("[ERROR] Could Not Find Mesh: ") + filename.svalue, ELL_ERROR); - return nullptr; + auto* bsdf = stack.top(); + stack.pop(); + // + switch (bsdf->type) + { + case CElementBSDF::COATING: + for (uint32_t i = 0u; i < bsdf->coating.childCount; ++i) + stack.push(bsdf->coating.bsdf[i]); + break; + case CElementBSDF::ROUGHCOATING: + case CElementBSDF::BUMPMAP: + case CElementBSDF::BLEND_BSDF: + case CElementBSDF::MIXTURE_BSDF: + case CElementBSDF::MASK: + case CElementBSDF::TWO_SIDED: + for (uint32_t i = 0u; i < bsdf->meta_common.childCount; ++i) + stack.push(bsdf->meta_common.bsdf[i]); + default: + break; + } + // + switch (bsdf->type) + { + case CElementBSDF::DIFFUSE: + case CElementBSDF::ROUGHDIFFUSE: + cachePropertyTexture(bsdf->diffuse.reflectance); + cachePropertyTexture(bsdf->diffuse.alpha); + break; + case CElementBSDF::DIFFUSE_TRANSMITTER: + cachePropertyTexture(bsdf->difftrans.transmittance); + break; + case CElementBSDF::DIELECTRIC: + case CElementBSDF::THINDIELECTRIC: + case CElementBSDF::ROUGHDIELECTRIC: + cachePropertyTexture(bsdf->dielectric.alphaU); + if (bsdf->dielectric.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) + cachePropertyTexture(bsdf->dielectric.alphaV); + break; + case CElementBSDF::CONDUCTOR: + case CElementBSDF::ROUGHCONDUCTOR: + cachePropertyTexture(bsdf->conductor.alphaU); + if (bsdf->conductor.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) + cachePropertyTexture(bsdf->conductor.alphaV); + break; + case CElementBSDF::PLASTIC: + case CElementBSDF::ROUGHPLASTIC: + cachePropertyTexture(bsdf->plastic.diffuseReflectance); + cachePropertyTexture(bsdf->plastic.alphaU); + if (bsdf->plastic.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) + cachePropertyTexture(bsdf->plastic.alphaV); + break; + case CElementBSDF::BUMPMAP: + cacheTexture(ctx,0u,bsdf->bumpmap.texture,bsdf->bumpmap.wasNormal ? CMitsubaMaterialCompilerFrontend::EIVS_NORMAL_MAP:CMitsubaMaterialCompilerFrontend::EIVS_BUMP_MAP); + break; + case CElementBSDF::BLEND_BSDF: + cachePropertyTexture(bsdf->blendbsdf.weight,CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT); + break; + case CElementBSDF::MASK: + cachePropertyTexture(bsdf->mask.opacity,CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT); + break; + default: break; + } + } + } + + return ctx.frontend.compileToIRTree(ctx.ir.get(), _bsdf); +} + + +// Also sets instance data buffer offset into meshbuffers' base instance +template +inline core::smart_refctd_ptr CMitsubaLoader::createDS0(const SContext& _ctx, asset::ICPUPipelineLayout* _layout, const asset::material_compiler::CMaterialCompilerGLSLBackendCommon::result_t& _compResult, Iter meshBegin, Iter meshEnd) +{ + +#ifdef DEBUG_MITSUBA_LOADER + std::ofstream ofile("log.txt"); +#endif + core::vector instanceData; + for (auto it=meshBegin; it != meshEnd; ++it) + { + auto mesh = it->first.get(); + + core::vectorSIMDf emissive; + for (auto& mb : mesh->getMeshBuffers()) + mb->setBaseInstance(instanceData.size()); + auto* meshMeta = _ctx.meta->getAssetSpecificMetadata(mesh); + auto baseInstanceDataIt = meshMeta->m_instances.begin(); + for (const auto& inst : meshMeta->m_instanceAuxData) + { + nbl_glsl_ext_Mitsuba_Loader_instance_data_t instData; + + instData.tform = baseInstanceDataIt->worldTform; + instData.tform.getSub3x3InverseTranspose(reinterpret_cast(instData.normalMatrixRow0)); + reinterpret_cast(instData.determinantSignBit) = instData.tform.getPseudoDeterminant().x; + instData.determinantSignBit &= 0x80000000; + + const auto& bsdf = inst.bsdf; + auto bsdf_front = bsdf.front; + auto bsdf_back = bsdf.back; + auto material_it = _compResult.materials.find(bsdf_front); + { + const asset::material_compiler::oriented_material_t* material = + (material_it != _compResult.materials.end()) ? &material_it->second : nullptr; + + if (material) { +#ifdef DEBUG_MITSUBA_LOADER + //os::Printer::log("Debug print front BSDF with id = ", std::to_string(&bsdf), ELL_INFORMATION); + + ofile << "Debug print front BSDF with id = " << &bsdf << std::endl; + _ctx.backend.debugPrint(ofile, *material, _compResult, &_ctx.backend_ctx); + +#endif + instData.material.front = *material; + } + } + material_it = _compResult.materials.find(bsdf_back); + { + const asset::material_compiler::oriented_material_t* material = + (material_it != _compResult.materials.end()) ? &material_it->second : nullptr; + + if (material) + { +#ifdef DEBUG_MITSUBA_LOADER + //os::Printer::log("Debug print back BSDF with id = ", std::to_string(&bsdf), ELL_INFORMATION); + ofile << "Debug print back BSDF with id = " << &bsdf << std::endl; + _ctx.backend.debugPrint(ofile, *material, _compResult, &_ctx.backend_ctx); +#endif + + instData.material.back = *material; + } + } + + instanceData.push_back(instData); + baseInstanceDataIt++; + } + } +#ifdef DEBUG_MITSUBA_LOADER + ofile.close(); +#endif + d = ds0->getDescriptors(INSTANCE_DATA_BINDING).begin(); + { + auto instDataBuf = core::make_smart_refctd_ptr(instanceData.size()*sizeof(nbl_glsl_ext_Mitsuba_Loader_instance_data_t)); + memcpy(instDataBuf->getPointer(), instanceData.data(), instDataBuf->getSize()); + + d->buffer.offset = 0u; + d->buffer.size = instDataBuf->getSize(); + d->desc = std::move(instDataBuf); + } + + return ds0; +} +#endif + +using namespace std::string_literals; + +SContext::SContext( +// const asset::IGeometryCreator* _geomCreator, +// const asset::IMeshManipulator* _manipulator, + const asset::IAssetLoader::SAssetLoadContext& _ctx, + asset::IAssetLoader::IAssetLoaderOverride* _override, + CMitsubaMetadata* _metadata +) : /*creator(_geomCreator), manipulator(_manipulator),*/ inner(_ctx), override_(_override), meta(_metadata) +//,ir(core::make_smart_refctd_ptr()), frontend(this) +{ + auto materialPool = material_compiler3::CTrueIR::create(); + scene = ICPUScene::create(core::smart_refctd_ptr(materialPool)); // TODO: feed it max shapes per group + frontIR = material_compiler3::CFrontendIR::create(); +} + +auto SContext::loadShapeGroup(const uint32_t hierarchyLevel, const CElementShape::ShapeGroup* shapegroup) -> SContext::group_ass_type +{ + auto found = groupCache.find(shapegroup); + if (found!=groupCache.end()) + return found->second; + + auto collection = core::make_smart_refctd_ptr(); + if (!collection) + inner.params.logger.log("Failed to create an ICPUGeometryCollection for Shape Group",system::ILogger::ELL_ERROR); + else + { + auto* geometries = collection->getGeometries(); + const auto children = shapegroup->children; + for (auto i=0u; ichildCount; i++) + { + auto child = children[i]; + if (!child) + continue; + + assert(child->type!=CElementShape::Type::INSTANCE); + if (child->type!=CElementShape::Type::SHAPEGROUP) + { + auto geometry = loadBasicShape(hierarchyLevel,child); + if (geometry) + geometries->push_back({.transform=child->getTransform(),.geometry=std::move(geometry)}); + } + else + { + auto nestedCollection = loadShapeGroup(hierarchyLevel,&child->shapegroup); + if (!nestedCollection) + continue; + auto* nestedGeometries = nestedCollection->getGeometries(); + for (auto& ref : *nestedGeometries) + { + auto& newRef = geometries->emplace_back(std::move(ref)); + // thankfully because SHAPEGROUPS are not allowed to have transforms we don't need to rack them up + //if (newRef.hasTransform()) + // newRef.transform = hlsl::mul(thisTransform,newRef.transform); + //else + // newRef.transform = thisTransform; + } + } + } + groupCache.insert({shapegroup,collection}); + } + return collection; +} + +#if 0 +static core::smart_refctd_ptr createMeshFromGeomCreatorReturnType(IGeometryCreator::return_type&& _data, asset::IAssetManager* _manager) +{ + //creating pipeline just to forward vtx and primitive params + auto pipeline = core::make_smart_refctd_ptr( + nullptr, nullptr, nullptr, //no layout nor shaders + _data.inputParams, + asset::SBlendParams(), + _data.assemblyParams, + asset::SRasterizationParams() + ); + + auto mb = core::make_smart_refctd_ptr( + nullptr, nullptr, + _data.bindings, std::move(_data.indexBuffer) + ); + mb->setIndexCount(_data.indexCount); + mb->setIndexType(_data.indexType); + mb->setBoundingBox(_data.bbox); + mb->setPipeline(std::move(pipeline)); + constexpr auto NORMAL_ATTRIBUTE = 3; + mb->setNormalAttributeIx(NORMAL_ATTRIBUTE); + + auto mesh = core::make_smart_refctd_ptr(); + mesh->getMeshBufferVector().push_back(std::move(mb)); + + return mesh; +} +#endif + +auto SContext::loadBasicShape(const uint32_t hierarchyLevel, const CElementShape* shape) -> SContext::shape_ass_type +{ + auto found = shapeCache.find(shape); + if (found!=shapeCache.end()) + return found->second.geom; + + core::smart_refctd_ptr geo; + auto exiter = core::makeRAIIExiter<>([&]()->void + { + if (geo) + return; + this->inner.params.logger.log("Failed to Load/Create Basic non-Instanced Shape with id %s",system::ILogger::ELL_ERROR,shape->id.c_str()); + } + ); + +#if 0 + constexpr uint32_t UV_ATTRIB_ID = 2u; + + + + auto loadModel = [&](const ext::MitsubaLoader::SPropertyElementData& filename, int64_t index=-1) -> core::smart_refctd_ptr + { + assert(filename.type==ext::MitsubaLoader::SPropertyElementData::Type::STRING); + auto loadParams = ctx.inner.params; + loadParams.loaderFlags = static_cast(loadParams.loaderFlags | IAssetLoader::ELPF_RIGHT_HANDED_MESHES); + auto retval = interm_getAssetInHierarchy( filename.svalue, loadParams, hierarchyLevel/*+ICPUScene::MESH_HIERARCHY_LEVELS_BELOW*/, ctx.override_); + if (retval.getContents().empty()) + { + os::Printer::log(std::string("[ERROR] Could Not Find Mesh: ") + filename.svalue, ELL_ERROR); + return nullptr; } if (retval.getAssetType()!=asset::IAsset::ET_MESH) { @@ -778,21 +772,21 @@ SContext::shape_ass_type CMitsubaLoader::loadBasicShape(SContext& ctx, uint32_t else return nullptr; }; - - core::smart_refctd_ptr mesh,newMesh; +#endif bool flipNormals = false; bool faceNormals = false; - float maxSmoothAngle = NAN; + float maxSmoothAngle = hlsl::bit_cast(hlsl::numeric_limits::quiet_NaN); switch (shape->type) { +#if 0 case CElementShape::Type::CUBE: { auto cubeData = ctx.creator->createCubeMesh(core::vector3df(2.f)); mesh = createMeshFromGeomCreatorReturnType(ctx.creator->createCubeMesh(core::vector3df(2.f)), m_assetMgr); flipNormals = flipNormals!=shape->cube.flipNormals; - } break; + } case CElementShape::Type::SPHERE: mesh = createMeshFromGeomCreatorReturnType(ctx.creator->createSphereMesh(1.f,64u,64u), m_assetMgr); flipNormals = flipNormals!=shape->sphere.flipNormals; @@ -834,6 +828,8 @@ SContext::shape_ass_type CMitsubaLoader::loadBasicShape(SContext& ctx, uint32_t mesh = createMeshFromGeomCreatorReturnType(ctx.creator->createDiskMesh(1.f,64u), m_assetMgr); flipNormals = flipNormals!=shape->disk.flipNormals; break; +#endif +#if 0 case CElementShape::Type::OBJ: mesh = loadModel(shape->obj.filename); flipNormals = flipNormals!=shape->obj.flipNormals; @@ -909,497 +905,64 @@ SContext::shape_ass_type CMitsubaLoader::loadBasicShape(SContext& ctx, uint32_t faceNormals = shape->serialized.faceNormals; maxSmoothAngle = shape->serialized.maxSmoothAngle; break; +#endif case CElementShape::Type::SHAPEGROUP: [[fallthrough]]; case CElementShape::Type::INSTANCE: assert(false); break; default: - _NBL_DEBUG_BREAK_IF(true); +// _NBL_DEBUG_BREAK_IF(true); break; } // - if (!mesh) - return nullptr; - - // mesh including meshbuffers needs to be cloned because instance counts and base instances will be changed - if (!newMesh) - newMesh = core::smart_refctd_ptr_static_cast(mesh->clone(1u)); - // flip normals if necessary - if (flipNormals) + if (geo) { - for (auto& meshbuffer : mesh->getMeshBufferVector()) +#if 0 + // mesh including meshbuffers needs to be cloned because instance counts and base instances will be changed + if (!newMesh) + newMesh = core::smart_refctd_ptr_static_cast(mesh->clone(1u)); + // flip normals if necessary + if (flipNormals) { - auto binding = meshbuffer->getIndexBufferBinding(); - binding.buffer = core::smart_refctd_ptr_static_cast(binding.buffer->clone(0u)); - meshbuffer->setIndexBufferBinding(std::move(binding)); - ctx.manipulator->flipSurfaces(meshbuffer.get()); + for (auto& meshbuffer : mesh->getMeshBufferVector()) + { + auto binding = meshbuffer->getIndexBufferBinding(); + binding.buffer = core::smart_refctd_ptr_static_cast(binding.buffer->clone(0u)); + meshbuffer->setIndexBufferBinding(std::move(binding)); + ctx.manipulator->flipSurfaces(meshbuffer.get()); + } } - } - // recompute normalis if necessary - if (faceNormals || !std::isnan(maxSmoothAngle)) - for (auto& meshbuffer : mesh->getMeshBufferVector()) - { - const float smoothAngleCos = cos(core::radians(maxSmoothAngle)); - - // TODO: make these mesh manipulator functions const-correct - auto newMeshBuffer = ctx.manipulator->createMeshBufferUniquePrimitives(meshbuffer.get()); - ctx.manipulator->filterInvalidTriangles(newMeshBuffer.get()); - ctx.manipulator->calculateSmoothNormals(newMeshBuffer.get(), false, 0.f, newMeshBuffer->getNormalAttributeIx(), - [&](const asset::IMeshManipulator::SSNGVertexData& a, const asset::IMeshManipulator::SSNGVertexData& b, asset::ICPUMeshBuffer* buffer) + // recompute normalis if necessary + if (faceNormals || !std::isnan(maxSmoothAngle)) + for (auto& meshbuffer : mesh->getMeshBufferVector()) { - if (faceNormals) - return a.indexOffset == b.indexOffset; - else - return core::dot(a.parentTriangleFaceNormal, b.parentTriangleFaceNormal).x >= smoothAngleCos; - }); - meshbuffer = std::move(newMeshBuffer); - } - IMeshManipulator::recalculateBoundingBox(newMesh.get()); - mesh = std::move(newMesh); + const float smoothAngleCos = cos(core::radians(maxSmoothAngle)); - addInstance(mesh); - // cache and return - ctx.shapeCache.insert({ shape,mesh }); - return mesh; + // TODO: make these mesh manipulator functions const-correct + auto newMeshBuffer = ctx.manipulator->createMeshBufferUniquePrimitives(meshbuffer.get()); + ctx.manipulator->filterInvalidTriangles(newMeshBuffer.get()); + ctx.manipulator->calculateSmoothNormals(newMeshBuffer.get(), false, 0.f, newMeshBuffer->getNormalAttributeIx(), + [&](const asset::IMeshManipulator::SSNGVertexData& a, const asset::IMeshManipulator::SSNGVertexData& b, asset::ICPUMeshBuffer* buffer) + { + if (faceNormals) + return a.indexOffset == b.indexOffset; + else + return core::dot(a.parentTriangleFaceNormal, b.parentTriangleFaceNormal).x >= smoothAngleCos; + }); + meshbuffer = std::move(newMeshBuffer); + } + IMeshManipulator::recalculateBoundingBox(newMesh.get()); + mesh = std::move(newMesh); +#endif + // cache and return + CMitsubaMetadata::SGeometryMetaPair geoMeta = {.geom=std::move(geo)}; + geoMeta.meta.m_id = shape->id; + geoMeta.meta.type = shape->type; + shapeCache.insert({shape,std::move(geoMeta)}); + } + return geo; } -void CMitsubaLoader::cacheEmissionProfile(SContext& ctx, const CElementEmissionProfile* profile) -{ - if (!profile) - return; - - auto params = ctx.inner.params; - params.loaderFlags = asset::IAssetLoader::ELPF_LOAD_METADATA_ONLY; - - auto assetLoaded = interm_getAssetInHierarchy(m_assetMgr, profile->filename, params, 0u, ctx.override_); - - if (!assetLoaded.getMetadata()) - { - os::Printer::log("[ERROR] Could Not Find Emission Profile: " + profile->filename, ELL_ERROR); - return; - } -} - -void CMitsubaLoader::cacheTexture(SContext& ctx, uint32_t hierarchyLevel, const CElementTexture* tex, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic) -{ - if (!tex) - return; - - switch (tex->type) - { - case CElementTexture::Type::BITMAP: - { - // get sampler parameters - const auto samplerParams = ctx.computeSamplerParameters(tex->bitmap); - - // search the cache for the imageview - const auto cacheKey = ctx.imageViewCacheKey(tex->bitmap,semantic); - const asset::IAsset::E_TYPE types[]{asset::IAsset::ET_IMAGE_VIEW,asset::IAsset::ET_TERMINATING_ZERO}; - // could not find view in the cache - if (ctx.override_->findCachedAsset(cacheKey,types,ctx.inner,hierarchyLevel).getContents().empty()) - { - ICPUImageView::SCreationParams viewParams = {}; - // find or restore image from cache - { - auto loadParams = ctx.inner.params; - // always restore, the only reason we haven't found a view is because either the image wasnt loaded yet, or its going to be processed with channel extraction or derivative mapping - const uint32_t restoreLevels = semantic==CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY&&tex->bitmap.channel==CElementTexture::Bitmap::CHANNEL::INVALID ? 0u:2u; // all the way to the buffer providing the pixels - loadParams.restoreLevels = std::max(loadParams.restoreLevels,hierarchyLevel+restoreLevels); - // load using the actual filename, not the cache key - asset::SAssetBundle bundle = interm_getAssetInHierarchy(m_assetMgr,tex->bitmap.filename.svalue,loadParams,hierarchyLevel,ctx.override_); - - // check if found - auto contentRange = bundle.getContents(); - if (contentRange.empty()) - { - os::Printer::log("[ERROR] Could Not Find Texture: "+cacheKey,ELL_ERROR); - return; - } - auto asset = contentRange.begin()[0]; - if (asset->getAssetType()!=asset::IAsset::ET_IMAGE) - { - os::Printer::log("[ERROR] Loaded an Asset but it wasn't a texture, was E_ASSET_TYPE "+std::to_string(asset->getAssetType()),ELL_ERROR); - return; - } - - viewParams.image = core::smart_refctd_ptr_static_cast(asset); - } - // adjust gamma on pixels (painful and long process) - if (!std::isnan(tex->bitmap.gamma)) - { - _NBL_DEBUG_BREAK_IF(true); // TODO : use an image filter (unify with the below maybe?)! - } - switch (semantic) - { - case CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY: - case CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT: - { - switch (tex->bitmap.channel) - { - // no GL_R8_SRGB support yet - case CElementTexture::Bitmap::CHANNEL::R: - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_R); - break; - case CElementTexture::Bitmap::CHANNEL::G: - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_G); - break; - case CElementTexture::Bitmap::CHANNEL::B: - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_B); - break; - case CElementTexture::Bitmap::CHANNEL::A: - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_A); - break; - /* special conversions needed to CIE space - case CElementTexture::Bitmap::CHANNEL::X: - case CElementTexture::Bitmap::CHANNEL::Y: - case CElementTexture::Bitmap::CHANNEL::Z:*/ - case CElementTexture::Bitmap::CHANNEL::INVALID: - [[fallthrough]]; - default: - if (semantic==CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT && asset::getFormatChannelCount(viewParams.image->getCreationParameters().format)<3u) - viewParams.image = createSingleChannelImage(viewParams.image.get(),asset::ICPUImageView::SComponentMapping::ES_IDENTITY); - break; - } - } - break; - case CMitsubaMaterialCompilerFrontend::EIVS_NORMAL_MAP: - viewParams.image = createDerivMap(ctx,viewParams.image.get(),samplerParams,true); - break; - case CMitsubaMaterialCompilerFrontend::EIVS_BUMP_MAP: - viewParams.image = createDerivMap(ctx,viewParams.image.get(),samplerParams,false); - break; - default: - _NBL_DEBUG_BREAK_IF(true); - assert(false); - break; - } - // get rest of view params and insert into cache - { - viewParams.flags = static_cast(0); - viewParams.viewType = IImageView::ET_2D; - viewParams.format = viewParams.image->getCreationParameters().format; - viewParams.subresourceRange.aspectMask = static_cast(0); - viewParams.subresourceRange.levelCount = viewParams.image->getCreationParameters().mipLevels; - viewParams.subresourceRange.layerCount = 1u; - //! TODO: this stuff (custom shader sampling code?) - _NBL_DEBUG_BREAK_IF(tex->bitmap.uoffset != 0.f); - _NBL_DEBUG_BREAK_IF(tex->bitmap.voffset != 0.f); - _NBL_DEBUG_BREAK_IF(tex->bitmap.uscale != 1.f); - _NBL_DEBUG_BREAK_IF(tex->bitmap.vscale != 1.f); - - asset::SAssetBundle viewBundle(nullptr,{ICPUImageView::create(std::move(viewParams))}); - ctx.override_->insertAssetIntoCache(std::move(viewBundle),cacheKey,ctx.inner,hierarchyLevel); - } - } - } - break; - case CElementTexture::Type::SCALE: - cacheTexture(ctx,hierarchyLevel,tex->scale.texture,semantic); - break; - default: - _NBL_DEBUG_BREAK_IF(true); - break; - } -} - -auto CMitsubaLoader::getBSDFtreeTraversal(SContext& ctx, const CElementBSDF* bsdf, const CElementEmitter* emitter, core::matrix4SIMD tform) -> SContext::bsdf_type -{ - if (!bsdf) - { - static auto blackBSDF = []() -> auto - { - CElementBSDF retval("nullptr BSDF"); - retval.type = CElementBSDF::Type::DIFFUSE, - retval.diffuse.reflectance = 0.f; - retval.diffuse.alpha = 0.f; - return retval; - }(); - bsdf = &blackBSDF; - } - - auto found = ctx.instrStreamCache.find(bsdf); - if (found == ctx.instrStreamCache.end()) - found = ctx.instrStreamCache.insert({ bsdf,genBSDFtreeTraversal(ctx, bsdf) }).first; - auto compiled_bsdf = found->second; - - // TODO cache the IR Node - CMitsubaMaterialCompilerFrontend::EmitterNode* emitterIRNode = nullptr; - if (emitter->type == CElementEmitter::AREA) - { - cacheEmissionProfile(ctx,emitter->area.emissionProfile); - emitterIRNode = ctx.frontend.createEmitterNode(ctx.ir.get(),emitter,tform); - } - - // A new root node gets made for every {bsdf,emitter} combo - using node_t = asset::material_compiler::IR::INode; - auto createNewRootNode = [&ctx,emitterIRNode](node_t* realBxDFRoot, node_t* emitter=nullptr) -> node_t* - { - // TODO: cache the combo! - auto newRoot = ctx.ir->allocNode(); - if (emitter) - newRoot->children = node_t::createChildrenArray(realBxDFRoot,emitter); - else - newRoot->children = node_t::createChildrenArray(realBxDFRoot); - ctx.ir->addRootNode(newRoot); - return newRoot; - }; - - return { createNewRootNode(compiled_bsdf.front,emitterIRNode), createNewRootNode(compiled_bsdf.back)}; -} - -auto CMitsubaLoader::genBSDFtreeTraversal(SContext& ctx, const CElementBSDF* _bsdf) -> SContext::bsdf_type -{ - { - auto cachePropertyTexture = [&](const auto& const_or_tex, const CMitsubaMaterialCompilerFrontend::E_IMAGE_VIEW_SEMANTIC semantic=CMitsubaMaterialCompilerFrontend::EIVS_IDENTITIY) -> void - { - if (const_or_tex.value.type==SPropertyElementData::INVALID) - cacheTexture(ctx,0u,const_or_tex.texture,semantic); - }; - - core::stack stack; - stack.push(_bsdf); - - while (!stack.empty()) - { - auto* bsdf = stack.top(); - stack.pop(); - // - switch (bsdf->type) - { - case CElementBSDF::COATING: - for (uint32_t i = 0u; i < bsdf->coating.childCount; ++i) - stack.push(bsdf->coating.bsdf[i]); - break; - case CElementBSDF::ROUGHCOATING: - case CElementBSDF::BUMPMAP: - case CElementBSDF::BLEND_BSDF: - case CElementBSDF::MIXTURE_BSDF: - case CElementBSDF::MASK: - case CElementBSDF::TWO_SIDED: - for (uint32_t i = 0u; i < bsdf->meta_common.childCount; ++i) - stack.push(bsdf->meta_common.bsdf[i]); - default: - break; - } - // - switch (bsdf->type) - { - case CElementBSDF::DIFFUSE: - case CElementBSDF::ROUGHDIFFUSE: - cachePropertyTexture(bsdf->diffuse.reflectance); - cachePropertyTexture(bsdf->diffuse.alpha); - break; - case CElementBSDF::DIFFUSE_TRANSMITTER: - cachePropertyTexture(bsdf->difftrans.transmittance); - break; - case CElementBSDF::DIELECTRIC: - case CElementBSDF::THINDIELECTRIC: - case CElementBSDF::ROUGHDIELECTRIC: - cachePropertyTexture(bsdf->dielectric.alphaU); - if (bsdf->dielectric.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) - cachePropertyTexture(bsdf->dielectric.alphaV); - break; - case CElementBSDF::CONDUCTOR: - case CElementBSDF::ROUGHCONDUCTOR: - cachePropertyTexture(bsdf->conductor.alphaU); - if (bsdf->conductor.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) - cachePropertyTexture(bsdf->conductor.alphaV); - break; - case CElementBSDF::PLASTIC: - case CElementBSDF::ROUGHPLASTIC: - cachePropertyTexture(bsdf->plastic.diffuseReflectance); - cachePropertyTexture(bsdf->plastic.alphaU); - if (bsdf->plastic.distribution == CElementBSDF::RoughSpecularBase::ASHIKHMIN_SHIRLEY) - cachePropertyTexture(bsdf->plastic.alphaV); - break; - case CElementBSDF::BUMPMAP: - cacheTexture(ctx,0u,bsdf->bumpmap.texture,bsdf->bumpmap.wasNormal ? CMitsubaMaterialCompilerFrontend::EIVS_NORMAL_MAP:CMitsubaMaterialCompilerFrontend::EIVS_BUMP_MAP); - break; - case CElementBSDF::BLEND_BSDF: - cachePropertyTexture(bsdf->blendbsdf.weight,CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT); - break; - case CElementBSDF::MASK: - cachePropertyTexture(bsdf->mask.opacity,CMitsubaMaterialCompilerFrontend::EIVS_BLEND_WEIGHT); - break; - default: break; - } - } - } - - return ctx.frontend.compileToIRTree(ctx.ir.get(), _bsdf); -} - - -// Also sets instance data buffer offset into meshbuffers' base instance -template -inline core::smart_refctd_ptr CMitsubaLoader::createDS0(const SContext& _ctx, asset::ICPUPipelineLayout* _layout, const asset::material_compiler::CMaterialCompilerGLSLBackendCommon::result_t& _compResult, Iter meshBegin, Iter meshEnd) -{ - auto* ds0layout = _layout->getDescriptorSetLayout(0u); - - auto ds0 = core::make_smart_refctd_ptr(core::smart_refctd_ptr(ds0layout)); - { - auto count = _ctx.backend_ctx.vt.getCPUVirtualTexture()->getDescriptorSetWrites(nullptr, nullptr, nullptr); - - auto writes = core::make_refctd_dynamic_array>(count.first); - auto info = core::make_refctd_dynamic_array>(count.second); - - _ctx.backend_ctx.vt.getCPUVirtualTexture()->getDescriptorSetWrites(writes->data(), info->data(), ds0.get()); - - for (const auto& w : (*writes)) - { - auto descRng = ds0->getDescriptors(w.binding); - for (uint32_t i = 0u; i < w.count; ++i) - descRng.begin()[w.arrayElement+i].assign(w.info[i], w.descriptorType); - } - } - auto d = ds0->getDescriptors(PRECOMPUTED_VT_DATA_BINDING).begin(); - { - auto precompDataBuf = core::make_smart_refctd_ptr(sizeof(asset::ICPUVirtualTexture::SPrecomputedData)); - memcpy(precompDataBuf->getPointer(), &_ctx.backend_ctx.vt.getCPUVirtualTexture()->getPrecomputedData(), precompDataBuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = precompDataBuf->getSize(); - d->desc = std::move(precompDataBuf); - } - d = ds0->getDescriptors(INSTR_BUF_BINDING).begin(); - { - auto instrbuf = core::make_smart_refctd_ptr(_compResult.instructions.size()*sizeof(decltype(_compResult.instructions)::value_type)); - memcpy(instrbuf->getPointer(), _compResult.instructions.data(), instrbuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = instrbuf->getSize(); - d->desc = std::move(instrbuf); - } - d = ds0->getDescriptors(BSDF_BUF_BINDING).begin(); - { - auto bsdfbuf = core::make_smart_refctd_ptr(_compResult.bsdfData.size()*sizeof(decltype(_compResult.bsdfData)::value_type)); - memcpy(bsdfbuf->getPointer(), _compResult.bsdfData.data(), bsdfbuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = bsdfbuf->getSize(); - d->desc = std::move(bsdfbuf); - } - d = ds0->getDescriptors(EMITTER_DATA_BUF_BINDING).begin(); - { - auto emitterbuf = core::make_smart_refctd_ptr(_compResult.emitterData.size() * sizeof(decltype(_compResult.emitterData)::value_type)); - memcpy(emitterbuf->getPointer(), _compResult.emitterData.data(), emitterbuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = emitterbuf->getSize(); - d->desc = std::move(emitterbuf); - } - d = ds0->getDescriptors(PREFETCH_INSTR_BUF_BINDING).begin(); - { - const size_t sz = _compResult.prefetch_stream.size()*sizeof(decltype(_compResult.prefetch_stream)::value_type); - - constexpr size_t MIN_SSBO_SZ = 128ull; //prefetch stream won't be generated if no textures are used, so make sure we're not creating 0-size buffer - auto prefetch_instr_buf = core::make_smart_refctd_ptr(std::max(MIN_SSBO_SZ, sz)); - memcpy(prefetch_instr_buf->getPointer(), _compResult.prefetch_stream.data(), sz); - - d->buffer.offset = 0u; - d->buffer.size = prefetch_instr_buf->getSize(); - d->desc = std::move(prefetch_instr_buf); - } - -#ifdef DEBUG_MITSUBA_LOADER - std::ofstream ofile("log.txt"); -#endif - core::vector instanceData; - for (auto it=meshBegin; it != meshEnd; ++it) - { - auto mesh = it->first.get(); - - core::vectorSIMDf emissive; - for (auto& mb : mesh->getMeshBuffers()) - mb->setBaseInstance(instanceData.size()); - auto* meshMeta = _ctx.meta->getAssetSpecificMetadata(mesh); - auto baseInstanceDataIt = meshMeta->m_instances.begin(); - for (const auto& inst : meshMeta->m_instanceAuxData) - { - nbl_glsl_ext_Mitsuba_Loader_instance_data_t instData; - - instData.tform = baseInstanceDataIt->worldTform; - instData.tform.getSub3x3InverseTranspose(reinterpret_cast(instData.normalMatrixRow0)); - reinterpret_cast(instData.determinantSignBit) = instData.tform.getPseudoDeterminant().x; - instData.determinantSignBit &= 0x80000000; - - const auto& bsdf = inst.bsdf; - auto bsdf_front = bsdf.front; - auto bsdf_back = bsdf.back; - auto material_it = _compResult.materials.find(bsdf_front); - { - const asset::material_compiler::oriented_material_t* material = - (material_it != _compResult.materials.end()) ? &material_it->second : nullptr; - - if (material) { -#ifdef DEBUG_MITSUBA_LOADER - //os::Printer::log("Debug print front BSDF with id = ", std::to_string(&bsdf), ELL_INFORMATION); - - ofile << "Debug print front BSDF with id = " << &bsdf << std::endl; - _ctx.backend.debugPrint(ofile, *material, _compResult, &_ctx.backend_ctx); - -#endif - instData.material.front = *material; - } - } - material_it = _compResult.materials.find(bsdf_back); - { - const asset::material_compiler::oriented_material_t* material = - (material_it != _compResult.materials.end()) ? &material_it->second : nullptr; - - if (material) - { -#ifdef DEBUG_MITSUBA_LOADER - //os::Printer::log("Debug print back BSDF with id = ", std::to_string(&bsdf), ELL_INFORMATION); - ofile << "Debug print back BSDF with id = " << &bsdf << std::endl; - _ctx.backend.debugPrint(ofile, *material, _compResult, &_ctx.backend_ctx); -#endif - - instData.material.back = *material; - } - } - - instanceData.push_back(instData); - baseInstanceDataIt++; - } - } -#ifdef DEBUG_MITSUBA_LOADER - ofile.close(); -#endif - d = ds0->getDescriptors(INSTANCE_DATA_BINDING).begin(); - { - auto instDataBuf = core::make_smart_refctd_ptr(instanceData.size()*sizeof(nbl_glsl_ext_Mitsuba_Loader_instance_data_t)); - memcpy(instDataBuf->getPointer(), instanceData.data(), instDataBuf->getSize()); - - d->buffer.offset = 0u; - d->buffer.size = instDataBuf->getSize(); - d->desc = std::move(instDataBuf); - } - - return ds0; -} - -using namespace std::string_literals; - -SContext::SContext( - const asset::IGeometryCreator* _geomCreator, - const asset::IMeshManipulator* _manipulator, - const asset::IAssetLoader::SAssetLoadContext& _ctx, - asset::IAssetLoader::IAssetLoaderOverride* _override, - CMitsubaMetadata* _metadata -) : creator(_geomCreator), manipulator(_manipulator), inner(_ctx), override_(_override), meta(_metadata), - ir(core::make_smart_refctd_ptr()), frontend(this) -{ - backend_ctx.vt = core::make_smart_refctd_ptr( - [](asset::E_FORMAT_CLASS) -> uint32_t { return VT_PHYSICAL_PAGE_TEX_TILES_PER_DIM_LOG2; }, // 16x16 tiles per layer for all dynamically created storages - VT_PAGE_SZ_LOG2, - VT_PAGE_PADDING, - VT_MAX_ALLOCATABLE_TEX_SZ_LOG2 - ); - meta->m_global.m_VT = core::smart_refctd_ptr(backend_ctx.vt.getCPUVirtualTexture()); -} - -} } } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp index a3c1c0949a..45a2592445 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp @@ -119,24 +119,9 @@ CMitsubaMaterialCompilerFrontend::EmitterNode* CMitsubaMaterialCompilerFrontend: profile.view = core::normalize(worldSpaceIESTransform[2]); } - float flatten = inProfile->flatten; - - // negative means full domain - const bool fullDomain = flatten < 0.f; - if (fullDomain) - flatten = -flatten; - - if (flatten > 1.f) - { - flatten = 1.f; - os::Printer::log("ERROR: Flatten property = " + std::to_string(inProfile->flatten) + " is outside it's [0, 1] domain, clamping!", ELL_ERROR); - } - else if (inProfile->flatten < std::numeric_limits::epsilon()-1) - os::Printer::log("WARNING: Full domain flatten mode detected with abs(flatten) = " + std::to_string(flatten), ELL_WARNING); - - core::smart_refctd_ptr flattenIES = nullptr; + core::smart_refctd_ptr iesTexture = nullptr; { - const auto cacheName = inProfile->filename + "?flatten=" + std::to_string(flatten); + const auto cacheName = inProfile->filename + "?ies"; // try cache { @@ -144,26 +129,26 @@ CMitsubaMaterialCompilerFrontend::EmitterNode* CMitsubaMaterialCompilerFrontend: asset::SAssetBundle bundle = m_loaderContext->override_->findCachedAsset(cacheName,types,m_loaderContext->inner,0u); auto contents = bundle.getContents(); if (!contents.empty() && bundle.getAssetType() == asset::IAsset::ET_IMAGE_VIEW) - flattenIES = core::smart_refctd_ptr_static_cast(*contents.begin()); + iesTexture = core::smart_refctd_ptr_static_cast(*contents.begin()); } // failed to find, have to create - if (!flattenIES) + if (!iesTexture) { const auto optimalResolution = meta->profile.getOptimalIESResolution(); - flattenIES = meta->profile.createIESTexture(flatten, fullDomain, optimalResolution.x, optimalResolution.y); + iesTexture = meta->profile.createIESTexture(optimalResolution); } // now must be loaded to proceed - if (!flattenIES) + if (!iesTexture) return false; // insert into cache asset::IAssetLoader::SAssetLoadContext ctx = { {}, nullptr }; - asset::SAssetBundle bundle = asset::SAssetBundle(nullptr, { core::smart_refctd_ptr(flattenIES) }); + asset::SAssetBundle bundle = asset::SAssetBundle(nullptr, { core::smart_refctd_ptr(iesTexture) }); m_loaderContext->override_->insertAssetIntoCache(bundle, cacheName, m_loaderContext->inner, 0u); } - profile.texture = { flattenIES, sampler, 1.f }; + profile.texture = { iesTexture, sampler, 1.f }; // success res->emissionProfile = profile; @@ -174,14 +159,11 @@ CMitsubaMaterialCompilerFrontend::EmitterNode* CMitsubaMaterialCompilerFrontend: { case CElementEmissionProfile::EN_UNIT_MAX: { - // true Max value changes because of flatten - // can be reverted back to do nothing if the TODO about adjusting max-value while flattening gets done - res->intensity *= maxIntesity/(maxIntesity+(meta->profile.getAvgEmmision(fullDomain)-maxIntesity)*flatten); + // already normalized to max } break; case CElementEmissionProfile::EN_UNIT_AVERAGE_OVER_IMPLIED_DOMAIN: { - // because negative flatten (`!fullDomain`) expands the domain so implied==full - res->intensity *= maxIntesity / meta->profile.getAvgEmmision(fullDomain); + res->intensity *= maxIntesity / meta->profile.getAvgEmmision(false); } break; case CElementEmissionProfile::EN_UNIT_AVERAGE_OVER_FULL_DOMAIN: { @@ -681,4 +663,4 @@ auto CMitsubaMaterialCompilerFrontend::compileToIRTree(asset::material_compiler: return { frontroot, backroot }; } -} \ No newline at end of file +} diff --git a/src/nbl/ext/MitsubaLoader/ElementMacros.h b/src/nbl/ext/MitsubaLoader/ElementMacros.h new file mode 100644 index 0000000000..3bbb997e6e --- /dev/null +++ b/src/nbl/ext/MitsubaLoader/ElementMacros.h @@ -0,0 +1,66 @@ +// 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/ext/MitsubaLoader/IElement.h" +#include "nbl/ext/MitsubaLoader/ParserUtil.h" + + +// Return value is if there's no error during the setting once basic checks are done +// For when you want to do custom handling of when property with string NAME and SNamedPropertyElement::Type::PROP_TYPE is getting added to this_t +#define NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY(NAME,PROP_TYPE) retval.registerCallback(SNamedPropertyElement::Type::PROP_TYPE,NAME,{\ + .func=[](this_t* _this, SNamedPropertyElement&& _property, const system::logger_opt_ptr logger)->bool + +// when you know that there's a member of `this_t` with identifier equal to NAME +#define NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY(NAME,PROP_TYPE) NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY(#NAME,PROP_TYPE) \ + {\ + static_assert(SNamedPropertyElement::Type::PROP_TYPE!=SNamedPropertyElement::Type::STRING); \ + _this->NAME = _property.getProperty(); \ + return true; \ + } \ + } \ +) + +// Similar to `NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY` but for `this_t` which declare `variant_list_t` (list of union types) +// this adds a compile-time filter against the constraint, such that only variant types matching the constraint are visited. +// Useful when multiple variants derive from the same base struct, or have the same member. +#define NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED(NAME,PROP_TYPE,CONSTRAINT,...) retval.template registerCallback( \ + SNamedPropertyElement::Type::PROP_TYPE,NAME,[](this_t* _this, SNamedPropertyElement&& _property, const system::logger_opt_ptr logger)->bool + +// TODO: docs +#define NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(NAME,PROP_TYPE,CONSTRAINT,...) NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED(#NAME,PROP_TYPE,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + {\ + bool success = false; \ + _this->visit([&_property,logger,&success](auto& state)->void \ + { \ + if constexpr (CONSTRAINT __VA_OPT__(,) __VA_ARGS__>::value) \ + { + +#define NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END \ + } \ + } \ + ); \ + return success; \ + } \ +) + +// This it to `NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY_CONSTRAINED` what `NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_PROPERTY` is to `NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_PROPERTY` +// So basically you know the member is the same across the constraint filtered types +#define NBL_EXT_MITSUBA_LOADER_REGISTER_SIMPLE_ADD_VARIANT_PROPERTY_CONSTRAINED(NAME,PROP_TYPE,CONSTRAINT,...) NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED(NAME,PROP_TYPE,CONSTRAINT __VA_OPT__(,) __VA_ARGS__) \ + static_assert(SNamedPropertyElement::Type::PROP_TYPE!=SNamedPropertyElement::Type::STRING); \ + state. ## NAME = _property.getProperty(); \ + success = true; \ +NBL_EXT_MITSUBA_LOADER_REGISTER_ADD_VARIANT_PROPERTY_CONSTRAINED_END + + +// just to reverse `is_base_of` +namespace nbl::ext::MitsubaLoader +{ +template +struct derived_from : std::is_base_of {}; +} + +#define NBL_EXT_MITSUBA_LOADER_ELEMENT_INVALID_TYPE_CHECK(NON_FATAL) if (type==Type::INVALID) \ +{ \ + invalidXMLFileStructure(logger,getLogName()+": type not specified"); \ + return NON_FATAL; \ +} \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/ParserUtil.cpp b/src/nbl/ext/MitsubaLoader/ParserUtil.cpp index 785fe622dc..e8b18d22a8 100644 --- a/src/nbl/ext/MitsubaLoader/ParserUtil.cpp +++ b/src/nbl/ext/MitsubaLoader/ParserUtil.cpp @@ -2,88 +2,100 @@ // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h + #include "nbl/ext/MitsubaLoader/ParserUtil.h" -#include "nbl/ext/MitsubaLoader/CElementFactory.h" +#include "nbl/ext/MitsubaLoader/CElementIntegrator.h" +#include "nbl/ext/MitsubaLoader/CElementSensor.h" +#include "nbl/ext/MitsubaLoader/CElementFilm.h" +#include "nbl/ext/MitsubaLoader/CElementRFilter.h" +#include "nbl/ext/MitsubaLoader/CElementSampler.h" +#include "nbl/ext/MitsubaLoader/CElementShape.h" +#include "nbl/ext/MitsubaLoader/CElementTransform.h" +//#include "nbl/ext/MitsubaLoader/CElementAnimation.h" +#include "nbl/ext/MitsubaLoader/CElementBSDF.h" +//#include "nbl/ext/MitsubaLoader/CElementTexture.h" +#include "nbl/ext/MitsubaLoader/CElementEmitter.h" +#include "nbl/ext/MitsubaLoader/CElementEmissionProfile.h" #include "expat/lib/expat.h" #include -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ - -system::logger_opt_ptr ParserLog::logger = nullptr; -void ParserLog::invalidXMLFileStructure(const std::string& errorMessage) +namespace nbl::ext::MitsubaLoader { - std::string message = "Mitsuba loader error - Invalid .xml file structure: \'" - + errorMessage + '\''; - - //ParserLog::logger.log(message, system::ILogger::E_LOG_LEVEL::ELL_ERROR); - _NBL_DEBUG_BREAK_IF(true); -} +using namespace nbl::system; -void ParserManager::elementHandlerStart(void* _data, const char* _el, const char** _atts) +auto ParserManager::parse(IFile* _file, const Params& _params) const -> Result { - auto ctx = *reinterpret_cast(_data); - - ctx.manager->parseElement(ctx, _el, _atts); + Result result = {}; + result.metadata = core::make_smart_refctd_ptr(); + SessionContext ctx = { + .result = &result, + .params = &_params, + .manager = this + }; + + if (!ctx.parse(_file)) + return {}; + + return result; } -void ParserManager::elementHandlerEnd(void* _data, const char* _el) +bool ParserManager::SessionContext::parse(IFile* _file) { - auto ctx = *reinterpret_cast(_data); - - ctx.manager->onEnd(ctx,_el); -} + auto logger = params->logger; - - -bool ParserManager::parse(system::IFile* _file, const system::logger_opt_ptr& _logger) -{ XML_Parser parser = XML_ParserCreate(nullptr); if (!parser) { - _logger.log("Could not create XML Parser!", system::ILogger::E_LOG_LEVEL::ELL_ERROR); + logger.log("Could not create XML Parser!",ILogger::E_LOG_LEVEL::ELL_ERROR); return false; } - XML_SetElementHandler(parser, elementHandlerStart, elementHandlerEnd); + XML_SetElementHandler(parser,elementHandlerStart,elementHandlerEnd); //from now data (instance of ParserData struct) will be visible to expat handlers - Context ctx = {this,parser,_file->getFileName().parent_path()/""}; - XML_SetUserData(parser, &ctx); - - - char* buff = (char*)_NBL_ALIGNED_MALLOC(_file->getSize(), 4096u); - - system::future future; - _file->read(future, (void*)buff, 0u, _file->getSize()); - future.get(); + XMLContext ctx = { + .session = this, + .currentXMLDir = _file->getFileName().parent_path()/"", + .parser = parser + }; + XML_SetUserData(parser,&ctx); + + const size_t size = _file->getSize(); + const void* const origPtr = const_cast(_file)->getMappedPointer(); + const char* buff = reinterpret_cast(origPtr); + if (!buff) + { + buff = reinterpret_cast(_NBL_ALIGNED_MALLOC(size,4096u)); + IFile::success_t success; + _file->read(success,const_cast(buff),0u,size); + if (!success) + { + logger.log("Could read the file into XML Parser Buffer!",ILogger::E_LOG_LEVEL::ELL_ERROR); + return false; + } + } + XML_Status parseStatus = XML_Parse(parser,buff,size,0); + if (origPtr!=buff) + _NBL_ALIGNED_FREE(const_cast(buff)); - XML_Status parseStatus = XML_Parse(parser, buff, _file->getSize(), 0); - _NBL_ALIGNED_FREE(buff); XML_ParserFree(parser); switch (parseStatus) { case XML_STATUS_ERROR: { - _logger.log("Parse status: XML_STATUS_ERROR", system::ILogger::E_LOG_LEVEL::ELL_ERROR); + logger.log("Parse status: XML_STATUS_ERROR",ILogger::E_LOG_LEVEL::ELL_ERROR); return false; } break; case XML_STATUS_OK: - #ifdef _NBL_DEBUG - _logger.log("Parse status: XML_STATUS_OK", system::ILogger::E_LOG_LEVEL::ELL_INFO); - #endif + logger.log("Parse status: XML_STATUS_OK",ILogger::E_LOG_LEVEL::ELL_INFO); break; case XML_STATUS_SUSPENDED: { - _logger.log("Parse status: XML_STATUS_SUSPENDED", system::ILogger::E_LOG_LEVEL::ELL_INFO); + logger.log("Parse status: XML_STATUS_SUSPENDED",ILogger::E_LOG_LEVEL::ELL_INFO); return false; } break; @@ -92,160 +104,410 @@ bool ParserManager::parse(system::IFile* _file, const system::logger_opt_ptr& _l return true; } -static const core::unordered_set propertyElements = { - "float", "string", "boolean", "integer", - "rgb", "srgb", "spectrum", "blackbody", - "point", "vector", - "matrix", "rotate", "translate", "scale", "lookat" -}; +void ParserManager::elementHandlerStart(void* _data, const char* _el, const char** _atts) +{ + auto& ctx = *reinterpret_cast(_data); -void ParserManager::parseElement(const Context& ctx, const char* _el, const char** _atts) + ctx.parseElement(_el,_atts); +} + +void ParserManager::XMLContext::killParseWithError(const std::string& message) const +{ + session->invalidXMLFileStructure(message); + XML_StopParser(parser,false); +} + +void ParserManager::XMLContext::parseElement(const char* _el, const char** _atts) { - if (core::strcmpi(_el, "scene") == 0) + if (core::strcmpi(_el,"scene")==0) { auto count = 0u; while (_atts && _atts[count]) { count++; } - if (count != 2u) + if (count!=2u) { - killParseWithError(ctx,"Wrong number of attributes for scene element"); + killParseWithError("Wrong number of attributes for scene element"); return; } - if (core::strcmpi(_atts[0], "version")) + if (core::strcmpi(_atts[0],"version")) { - ParserLog::invalidXMLFileStructure(std::string(_atts[0]) + " is not an attribute of scene element"); + session->invalidXMLFileStructure(core::string(_atts[0]) + " is not an attribute of scene element"); return; } - else if (core::strcmpi(_atts[1], "0.5.0")) + else if (core::strcmpi(_atts[1],"0.5.0")) { - ParserLog::invalidXMLFileStructure("Version " + std::string(_atts[1]) + " is unsupported"); + session->invalidXMLFileStructure("Version " + core::string(_atts[1]) + " is unsupported"); return; } - m_sceneDeclCount++; + session->sceneDeclCount++; return; } - if (m_sceneDeclCount==0u) + if (session->sceneDeclCount==0u) { - killParseWithError(ctx,"there is no scene element"); + killParseWithError("there is no scene element"); return; } - if (core::strcmpi(_el, "include") == 0) + const ParserManager* manager = session->manager; + if (core::strcmpi(_el,"include")==0) { - system::ISystem::future_t> future; - bool validInput = m_system->createFile(future, ctx.currentXMLDir.string()+_atts[1], system::IFile::ECF_READ); - if (!validInput) // try global path - validInput = m_system->createFile(future, _atts[1], system::IFile::ECF_READ); - if (!validInput) + core::smart_refctd_ptr file; + auto tryOpen = [&](const system::path& path)->bool { - ParserLog::invalidXMLFileStructure(std::string("Could not open include file: ") + _atts[1]); + for (auto i=0; i<2; i++) + { + ISystem::future_t> future; + auto flags = IFile::ECF_READ; + if (i==0) + flags |= IFile::ECF_MAPPABLE; + session->params->system->createFile(future,currentXMLDir/_atts[1],flags); + if (future.wait()) + future.acquire().move_into(file); + if (file) + return true; + } + return false; + }; + // first try as relative path, then as global + if (!tryOpen(currentXMLDir/_atts[1])) + if (!tryOpen(_atts[1])) + { + session->invalidXMLFileStructure(std::string("Could not open include file: ")+_atts[1]); return; } - auto file = future.get(); - parse(file.get(), system::logger_opt_ptr(nullptr)); // TODO: fix + if (!session->parse(file.get())) + killParseWithError(core::string("Could not parse include file: ")+_atts[1]); return; } + const auto& propertyElements = manager->propertyElements; if (propertyElements.find(_el)!=propertyElements.end()) { - processProperty(ctx, _el, _atts); + auto& elements = session->elements; + if (elements.empty()) + { + killParseWithError("cannot set a property with no element on the stack."); + return; + } + auto* element = elements.top().element; + if (!element) + { + session->invalidXMLFileStructure("cannot set property on element that failed to be created."); + return; + } + + auto optProperty = manager->propertyElementManager.createPropertyData(_el,_atts,session->params->logger); + if (!optProperty.has_value()) + { + session->invalidXMLFileStructure("could not create property data."); + return; + } + + bool unsupportedElement = true; + auto run = [&](const auto& map)->void + { + using element_t = std::remove_cvref_t::element_type; + if (element_t::ElementType==element->getType()) + { + unsupportedElement = false; + auto& property = optProperty.value(); + const auto& typeMap = map.byPropertyType[property.type]; + if (typeMap.empty()) + { + session->invalidXMLFileStructure("There's no property supported by ElementType (TODO) with PropertyType (TODO)"); + return; + } + auto nameIt = typeMap.find(property.name); + // special, find callback that matches all names (registered with empty name) + if (nameIt==typeMap.end()) + nameIt = typeMap.find(""); + if (nameIt==typeMap.end()) + { + core::string msg = "There's no Property named \""+property.name+"\" of Type "; + msg += property.type+" supported by <"+element->getLogName()+">"; + session->invalidXMLFileStructure(msg); + return; + } + const auto& callback = nameIt->second; + auto* typedElement = static_cast(element); + if constexpr (!std::is_same_v) + if (!callback.allowedVariantTypes.empty() && std::find(callback.allowedVariantTypes.begin(),callback.allowedVariantTypes.end(),typedElement->type)==callback.allowedVariantTypes.end()) + { + core::string msg = "There's no Property named \""+property.name+"\" of Type "; + msg += property.type+" supported by <"+element->getLogName()+"> of Variant "; + msg += std::to_string(typedElement->type); + session->invalidXMLFileStructure(msg); + return; + } + callback(typedElement,std::move(property),session->params->logger); + } + }; + std::apply([&run](const auto&... maps)->void + { + (run(maps), ...); + },manager->addPropertyMaps + ); + if (unsupportedElement) + { + const core::string typeName; // TODO = system::to_string(element->getType()); + session->invalidXMLFileStructure("Current Element Type "+typeName+" doesn't have a AddPropertyMap at all (no property adding supported)!"); + return; + } return; } - const auto& _map = CElementFactory::createElementTable; + // TODO: don't have this table be a global + const auto& _map = manager->createElementTable; auto found = _map.find(_el); if (found==_map.end()) { - ParserLog::invalidXMLFileStructure(std::string("Could not process element ") + _el); - elements.push({nullptr,""}); + session->invalidXMLFileStructure(std::string("Could not process element ")+_el); + session->elements.push({nullptr,""}); return; } - auto el = found->second.first(_atts, this); - bool goesOnStack = found->second.second; - if (!goesOnStack) + auto created = found->second.create(_atts,session); + // we still push nullptr (failed creation) onto the stack, we only stop parse on catastrphic failure + if (!found->second.retvalGoesOnStack) return; - - - elements.push(el); - if (el.first && el.first->id.size()) - handles[el.first->id] = el.first; + if (created.element && created.element->id.size()) + session->handles[created.element->id] = created.element; + session->elements.push(std::move(created)); } -void ParserManager::processProperty(const Context& ctx, const char* _el, const char** _atts) +void ParserManager::elementHandlerEnd(void* _data, const char* _el) { - if (elements.empty()) - { - killParseWithError(ctx,"cannot set a property with no element on the stack."); - return; - } - if (!elements.top().first) - { - ParserLog::invalidXMLFileStructure("cannot set property on element that failed to be created."); - return; - } + auto& ctx = *reinterpret_cast(_data); - auto optProperty = CPropertyElementManager::createPropertyData(_el, _atts); - - if (optProperty.first == false) - { - ParserLog::invalidXMLFileStructure("could not create property data."); - return; - } - - elements.top().first->addProperty(std::move(optProperty.second)); - - return; + ctx.onEnd(_el); } -void ParserManager::onEnd(const Context& ctx, const char* _el) +void ParserManager::XMLContext::onEnd(const char* _el) { - if (propertyElements.find(_el) != propertyElements.end()) + const auto& propertyElements = session->manager->propertyElements; + if (propertyElements.find(_el)!=propertyElements.end()) return; - if (core::strcmpi(_el, "scene") == 0) + if (core::strcmpi(_el,"scene")==0) { - m_sceneDeclCount--; + session->sceneDeclCount--; return; } + auto& elements = session->elements; if (elements.empty()) return; - auto element = elements.top(); elements.pop(); - if (element.first && !element.first->onEndTag(m_override,m_metadata.get())) + auto& result = *session->result; + if (element.element && !element.element->onEndTag(result.metadata.get(),session->params->logger)) { - killParseWithError(ctx,element.first->getLogName() + " could not onEndTag"); + killParseWithError(element.element->getLogName()+" could not onEndTag"); return; } if (!elements.empty()) { - IElement* parent = elements.top().first; - if (parent && !parent->processChildData(element.first, element.second)) + IElement* parent = elements.top().element; + if (parent && !parent->processChildData(element.element,element.name,session->params->logger)) { - if (element.first) - killParseWithError(ctx,element.first->getLogName() + " could not processChildData with name: " + element.second); + if (element.element) + killParseWithError("<"+parent->getLogName()+ "> could not processChildData of <"+element.element->getLogName()+"> with name: "+element.name); else - killParseWithError(ctx,"Failed to add a nullptr child with name: " + element.second); + killParseWithError("Failed to add a nullptr child with name: "+element.name); } return; } - if (element.first && element.first->getType()==IElement::Type::SHAPE) + // TODO: only allow this for top level elements so we don't load unused shapes + if (element.element) + switch (element.element->getType()) { - auto shape = static_cast(element.first); - if (shape) - shapegroups.emplace_back(shape,std::move(element.second)); + case IElement::Type::EMITTER: + { + auto emitter = static_cast(element.element); + if (emitter->type!=CElementEmitter::Type::CONSTANT) + result.emitters.emplace_back(emitter,std::move(element.name)); + else + result.ambient += emitter->constant.radiance; + break; + } + case IElement::Type::SHAPE: + { + auto shape = static_cast(element.element); + result.shapegroups.emplace_back(shape,std::move(element.name)); + break; + } + default: + break; } } +// special specs +template<> +struct ParserManager::CreateElement +{ + static inline SessionContext::named_element_t __call(const char** _atts, SessionContext* ctx) + { + if (IElement::invalidAttributeCount(_atts,2u)) + return {}; + if (core::strcmpi(_atts[0],"name")) + return {}; + + return {ctx->result->objects->construct(),_atts[1]}; + }; +}; +template<> +struct ParserManager::CreateElement +{ + static inline SessionContext::named_element_t __call(const char** _atts, SessionContext* ctx) + { + const char* type; + const char* id; + std::string name; + if (!IElement::getTypeIDAndNameStrings(type, id, name, _atts)) + return {}; + + CElementEmissionProfile* obj = ctx->result->objects->construct(id); + if (!obj) + return {}; + + return { obj,std::move(name) }; + }; +}; +// default spec +template +concept HasTypeMap = requires() { + { T::compStringToTypeMap() } -> std::same_as>; +}; +template +concept HasVisit = requires() { + { std::declval().visit([](auto& selfV)->void {}) } -> std::same_as; +}; +template requires HasTypeMap +struct ParserManager::CreateElement +{ + static inline SessionContext::named_element_t __call(const char** _atts, SessionContext* ctx) + { + const char* type; + const char* id; + std::string name; + if (!IElement::getTypeIDAndNameStrings(type,id,name,_atts)) + return {}; + + static const auto StringToTypeMap = Element::compStringToTypeMap(); // TODO: make a const member cause of DLL delay load + auto found = StringToTypeMap.find(type); + if (found==StringToTypeMap.end()) + { + ctx->invalidXMLFileStructure(core::string("unknown type in <")+std::to_string((uint8_t)Element::ElementType)+" type=\""+type+"\">"); + return {}; + } + + Element* obj = ctx->result->objects->construct(id); + if (!obj) + return {}; + + obj->type = found->second; + if constexpr (HasVisit) + obj->visit([](auto& selfV)->void + { + selfV = {}; + } + ); + else + obj->initialize(); + return {obj,std::move(name)}; + } +}; + +// +ParserManager::ParserManager() : propertyElements({ + "float", "string", "boolean", "integer", + "rgb", "srgb", "spectrum", "blackbody", + "point", "vector", + "matrix", "rotate", "translate", "scale", "lookat" +}), propertyElementManager(), createElementTable({ + {"integrator", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"sensor", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"film", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"rfilter", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"sampler", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"shape", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"transform", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, +// {"animation", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"bsdf", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"texture", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"emitter", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"emissionprofile", {.create=ParserManager::CreateElement::__call,.retvalGoesOnStack=true}}, + {"alias", {.create=processAlias,.retvalGoesOnStack=true}}, + {"ref", {.create=processRef,.retvalGoesOnStack=true}} +}), addPropertyMaps({ + CElementIntegrator::compAddPropertyMap(), + CElementSensor::compAddPropertyMap(), + CElementFilm::compAddPropertyMap(), + CElementRFilter::compAddPropertyMap(), + CElementSampler::compAddPropertyMap(), + CElementShape::compAddPropertyMap(), + CElementTransform::compAddPropertyMap(), + CElementBSDF::compAddPropertyMap(), + CElementTexture::compAddPropertyMap(), + CElementEmitter::compAddPropertyMap(), + CElementEmissionProfile::compAddPropertyMap() +}) { } + +auto ParserManager::processAlias(const char** _atts, SessionContext* ctx) -> SessionContext::named_element_t +{ + const char* id = nullptr; + const char* as = nullptr; + if (IElement::areAttributesInvalid(_atts,4u)) + { + ctx->invalidXMLFileStructure("Invalid attributes for "); + return {}; + } + + core::string name; + while (*_atts) + { + if (core::strcmpi(_atts[0], "id")==0) + id = _atts[1]; + else if (core::strcmpi(_atts[0], "as")==0) + as = _atts[1]; + else if (core::strcmpi(_atts[0], "name")==0) + name = _atts[1]; + _atts += 2; + } + // not finding the alias doesn't kill XML parse + if (!id || !as) + { + ctx->invalidXMLFileStructure("Alias ID and what we're aliasing is not found"); + return {nullptr,std::move(name)}; + } + + auto& handles = ctx->handles; + auto* original = handles[id]; + handles[as] = original; + return {original,std::move(name)}; } + +auto ParserManager::processRef(const char** _atts, SessionContext* ctx) -> SessionContext::named_element_t +{ + const char* id; + std::string name; + if (!IElement::getIDAndName(id,name,_atts)) + { + ctx->invalidXMLFileStructure("Malformed `` element!"); + return {nullptr,std::move(name)}; + } + + auto* original = ctx->handles[id]; + if (!original) + ctx->invalidXMLFileStructure(core::string("Used a `` element but referenced element not defined in preceeding XML!"); + return {original,std::move(name)}; } + } \ No newline at end of file diff --git a/src/nbl/ext/MitsubaLoader/PropertyElement.cpp b/src/nbl/ext/MitsubaLoader/PropertyElement.cpp index 66f0e40343..5ea0353a48 100644 --- a/src/nbl/ext/MitsubaLoader/PropertyElement.cpp +++ b/src/nbl/ext/MitsubaLoader/PropertyElement.cpp @@ -2,51 +2,24 @@ // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#include "quaternion.h" -#include "matrix3x4SIMD.h" -#include "matrix4SIMD.h" -#include "nbl/asset/format/decodePixels.h" + +//#include "quaternion.h" +//#include "matrix3x4SIMD.h" +//#include "matrix4SIMD.h" +//#include "nbl/asset/format/decodePixels.h" #include "nbl/ext/MitsubaLoader/PropertyElement.h" #include "nbl/ext/MitsubaLoader/ParserUtil.h" -namespace nbl -{ -namespace ext -{ -namespace MitsubaLoader -{ +#include "nbl/builtin/hlsl/math/linalg/transform.hlsl" +#include "nbl/builtin/hlsl/math/quaternions.hlsl" +#include "glm/gtc/matrix_transform.hpp" -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return fvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return ivalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return bvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return svalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return vvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return vvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return vvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return vvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return vvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return mvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return mvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return mvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return mvalue; } -template<> const typename SPropertyElementData::get_typename::type& SPropertyElementData::getProperty() const -{ return mvalue; } -const core::unordered_map SPropertyElementData::StringToType = { +namespace nbl::ext::MitsubaLoader +{ + +CPropertyElementManager::CPropertyElementManager() : StringToType({ {"float", SPropertyElementData::Type::FLOAT}, {"integer", SPropertyElementData::Type::INTEGER}, {"boolean", SPropertyElementData::Type::BOOLEAN}, @@ -62,38 +35,31 @@ const core::unordered_map CPropertyElementManager::createPropertyData(const char* _el, const char** _atts) + +std::optional CPropertyElementManager::createPropertyData(const char* _el, const char** _atts, system::logger_opt_ptr logger) const { - SNamedPropertyElement result(_el); + SNamedPropertyElement result = {}; + auto found = StringToType.find(_el); + if (found!=StringToType.end()) + result.type = found->second; + // initialization returns strings from `_atts` which match expected attributes const char* desiredAttributes[SPropertyElementData::MaxAttributes] = { nullptr }; - if (!result.initialize(_atts, desiredAttributes)) + if (!result.initialize(_atts,desiredAttributes)) { - _NBL_DEBUG_BREAK_IF(true); - return std::make_pair(false, SNamedPropertyElement()); + invalidXMLFileStructure(logger,"Failed to Intialize Named Property Element."); + return {}; } - bool success = true; - #define FAIL_IF_ATTRIBUTE_NULL(N) if (!desiredAttributes[N]) {success = false; break;} + auto printFailure = [&](const uint8_t attrId)->void{invalidXMLFileStructure(logger,"invalid element, name:\'"+result.name+"\' value:\'"+desiredAttributes[attrId]+"\'");}; + + #define FAIL_IF_ATTRIBUTE_NULL(N) if (!desiredAttributes[N]) \ + { \ + invalidXMLFileStructure(logger,"Invalid element, name:\'"+result.name+"\' Attribute #"+std::to_string(N)+"not found"); \ + return {}; \ + } switch (result.type) { case SPropertyElementData::Type::FLOAT: @@ -106,35 +72,52 @@ std::pair CPropertyElementManager::createPropertyDa break; case SPropertyElementData::Type::BOOLEAN: FAIL_IF_ATTRIBUTE_NULL(0u) - result.bvalue = retrieveBooleanValue(desiredAttributes[0],success); + if (auto ret=retrieveBooleanValue(desiredAttributes[0],logger); ret.has_value()) + result.bvalue = ret.value(); + else + { + printFailure(0); + return {}; + } break; case SPropertyElementData::Type::STRING: FAIL_IF_ATTRIBUTE_NULL(0u) { auto len = strlen(desiredAttributes[0]); - auto* tmp = (char*)_NBL_ALIGNED_MALLOC(len + 1u, 64u); - strcpy(tmp, desiredAttributes[0]); tmp[len] = 0; + auto* tmp = (char*)_NBL_ALIGNED_MALLOC(len+1u,64u); + strcpy(tmp,desiredAttributes[0]); tmp[len]=0; result.svalue = tmp; } break; case SPropertyElementData::Type::RGB: FAIL_IF_ATTRIBUTE_NULL(0u) - result.vvalue = retrieveVector(desiredAttributes[0], success); + result.vvalue = retrieveVector(desiredAttributes[0],logger); + if (core::isnan(result.vvalue[0])) + { + printFailure(0); + return {}; + } break; case SPropertyElementData::Type::SRGB: FAIL_IF_ATTRIBUTE_NULL(0u) { - bool tryVec = true; - result.vvalue = retrieveVector(desiredAttributes[0], tryVec); - if (!tryVec) - result.vvalue = retrieveHex(desiredAttributes[0], success); + result.vvalue = retrieveVector(desiredAttributes[0],logger); + if (core::isnan(result.vvalue[0])) + { + result.vvalue = retrieveHex(desiredAttributes[0],logger); + if (core::isnan(result.vvalue[0])) + { + printFailure(0); + return {}; + } + } for (auto i=0; i<3u; i++) result.vvalue[i] = core::srgb2lin(result.vvalue[i]); result.type = SPropertyElementData::Type::RGB; // now its an RGB value } break; case SPropertyElementData::Type::VECTOR: - result.vvalue.set(core::nan(),core::nan(),core::nan(),core::nan()); + result.vvalue = hlsl::float32_t4(core::nan()); for (auto i=0u; i<4u; i++) { if (desiredAttributes[i]) @@ -144,31 +127,46 @@ std::pair CPropertyElementManager::createPropertyDa // once a component is missing, the rest need to be missing too for (auto j=i+1; j<4u; j++) if (desiredAttributes[j]) - success = false; + { + printFailure(0); + return {}; + } break; } } break; case SPropertyElementData::Type::POINT: - result.vvalue.set(0.f, 0.f, 0.f); + result.vvalue = hlsl::float32_t4(0.f,0.f,0.f,core::nan()); for (auto i=0u; i<3u; i++) { if (desiredAttributes[i]) result.vvalue[i] = atof(desiredAttributes[i]); else { - success = false; - break; + printFailure(0); + return {}; } } break; case SPropertyElementData::Type::SPECTRUM: - assert(!desiredAttributes[1]); // no intent, TODO - assert(!desiredAttributes[2]); // does not come from a file + if (desiredAttributes[1]||desiredAttributes[2]) + { + invalidXMLFileStructure(logger,"Spectrum intent and loading from file unsupported!"); + return {}; + } { - std::string data(desiredAttributes[0]); - assert(data.find(':')==std::string::npos); // no hand specified wavelengths - result.vvalue = retrieveVector(data,success); // TODO: convert between mitsuba spectral buckets and Rec. 709 + std::string_view data(desiredAttributes[0]); + if (data.find(':')!=std::string::npos) + { + invalidXMLFileStructure(logger,"Manually specified wavelengths for spectral curve knots are unsupported!"); + return {}; + } + result.vvalue = retrieveVector(data,logger); // TODO: convert between mitsuba spectral buckets and Rec. 709 + if (core::isnan(result.vvalue[0])) + { + printFailure(0); + return {}; + } } break; case SPropertyElementData::Type::BLACKBODY: @@ -176,66 +174,79 @@ std::pair CPropertyElementManager::createPropertyDa break; case SPropertyElementData::Type::MATRIX: FAIL_IF_ATTRIBUTE_NULL(0u) - result.mvalue = retrieveMatrix(desiredAttributes[0],success); + result.mvalue = retrieveMatrix(desiredAttributes[0],logger); + if (core::isnan(result.mvalue[0][0])) + { + printFailure(0); + return {}; + } break; case SPropertyElementData::Type::TRANSLATE: - result.vvalue.set(0.f, 0.f, 0.f); + result.mvalue = hlsl::float32_t4x4(1.f); + // we're a bit more lax about what items we need present for (auto i=0u; i<3u; i++) if (desiredAttributes[i]) - result.vvalue[i] = atof(desiredAttributes[i]); - { - core::matrix3x4SIMD m; - m.setTranslation(result.vvalue); - result.mvalue = core::matrix4SIMD(m); - } + result.mvalue[i][3] = atof(desiredAttributes[i]); break; case SPropertyElementData::Type::ROTATE: FAIL_IF_ATTRIBUTE_NULL(0u) // have to have an angle - result.vvalue.set(0.f, 0.f, 0.f); - for (auto i=0u; i<3u; i++) - if (desiredAttributes[i+1]) - result.vvalue[i] = atof(desiredAttributes[i+1]); - if ((core::vectorSIMDf(0.f) == result.vvalue).all()) - { - success = false; - break; - } - result.vvalue = core::normalize(result.vvalue); + result.mvalue = hlsl::float32_t4x4(1.f); { - core::matrix3x4SIMD m; - m.setRotation(core::quaternion::fromAngleAxis(core::radians(atof(desiredAttributes[0])),result.vvalue)); - result.mvalue = core::matrix4SIMD(m); + auto axis = hlsl::float32_t3(0.f); + // again some laxness + for (auto i=0u; i<3u; i++) + if (desiredAttributes[i+1]) + axis[i] = atof(desiredAttributes[i+1]); + axis = hlsl::normalize(axis); + if (core::isnan(axis.x)) + { + invalidXMLFileStructure(logger,"Invalid element, name:\'"+result.name+"\' Axis can't be (0,0,0)"); + return {}; + } + using namespace nbl::hlsl::math; + const auto rotation = quaternion::create(axis, hlsl::radians(atof(desiredAttributes[0]))); + result.mvalue = linalg::promote_affine<4,4>(hlsl::_static_cast(rotation)); } break; case SPropertyElementData::Type::SCALE: - result.vvalue.set(1.f, 1.f, 1.f); - if (desiredAttributes[0]) + result.mvalue = hlsl::float32_t4x4(1.f); + if (desiredAttributes[0]) // you either get this one attribute { - float uniformScale = atof(desiredAttributes[0]); - result.vvalue.set(uniformScale, uniformScale, uniformScale); + const float uniformScale = atof(desiredAttributes[0]); + for (auto i=0u; i<3u; i++) + result.mvalue[i][i] = uniformScale; } - else - for (auto i=0u; i<3u; i++) - if (desiredAttributes[i+1u]) - result.vvalue[i] = atof(desiredAttributes[i+1u]); + else // or x,y,z { - core::matrix3x4SIMD m; - m.setScale(result.vvalue); - result.mvalue = core::matrix4SIMD(m); + for (auto i=0u; i<3u; i++) + if (desiredAttributes[i+1u]) + result.mvalue[i][i] = atof(desiredAttributes[i+1u]); } break; case SPropertyElementData::Type::LOOKAT: FAIL_IF_ATTRIBUTE_NULL(0u) FAIL_IF_ATTRIBUTE_NULL(1u) + result.mvalue = hlsl::float32_t4x4(1.f); { - core::vectorSIMDf origin,target,up; - origin = retrieveVector(desiredAttributes[0u], success); - target = retrieveVector(desiredAttributes[1u], success); + const hlsl::float32_t3 origin = retrieveVector(desiredAttributes[0u],logger).xyz; + if (core::isnan(origin.x)) + { + printFailure(0); + return {}; + } + const hlsl::float32_t3 target = retrieveVector(desiredAttributes[1u],logger).xyz; + if (core::isnan(target.x)) + { + printFailure(1); + return {}; + } + auto up = hlsl::float32_t3(core::nan()); if (desiredAttributes[2u]) - up = retrieveVector(desiredAttributes[2u],success); - else + up = retrieveVector(desiredAttributes[2u],logger).xyz; + if (core::isnan(up.x)) { - auto viewDirection = target - origin; + up = hlsl::float32_t3(0.f); + const auto viewDirection = target - origin; float maxDot = viewDirection[0]; uint32_t index = 0u; for (auto i = 1u; i < 3u; i++) @@ -246,75 +257,73 @@ std::pair CPropertyElementManager::createPropertyDa } up[index] = 1.f; } + // TODO: after the rm-core matrix PR we need to get rid of the tranpose (I transpose only because of GLM and HLSL mixup) + const auto lookAtGLM = reinterpret_cast(glm::lookAtLH(origin,target,up)); + const auto lookAt = hlsl::transpose(lookAtGLM); // mitsuba understands look-at and right-handed camera little bit differently than I do - core::matrix4SIMD(core::matrix3x4SIMD::buildCameraLookAtMatrixLH(origin,target,up)).getInverseTransform(result.mvalue); + const auto rotation = hlsl::inverse(hlsl::float32_t3x3(lookAt)); + // set the origin to avoid numerical issues + for (auto r=0; r<3; r++) + { + result.mvalue[r][3] = origin[r]; + } } break; default: - success = false; - break; + invalidXMLFileStructure(logger,"Unsupported element type, name:\'"+result.name+"\'"); + return {}; } + #undef FAIL_IF_ATTRIBUTE_NULL - _NBL_DEBUG_BREAK_IF(!success); - if (success) - return std::make_pair(true, std::move(result)); - - ParserLog::invalidXMLFileStructure("invalid element, name:\'" + result.name + "\'"); // in the future print values - return std::make_pair(false, SNamedPropertyElement()); + return result; } -bool CPropertyElementManager::retrieveBooleanValue(const std::string& _data, bool& success) +std::optional CPropertyElementManager::retrieveBooleanValue(const std::string_view& _data, system::logger_opt_ptr logger) { - if (_data == "true") - { + if (_data=="true") return true; - } - else if (_data == "false") - { + else if (_data=="false") return false; - } else { - _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("Invalid boolean specified."); - success = false; - return false; // so GCC doesn't moan + invalidXMLFileStructure(logger,"Invalid boolean specified."); + return {}; } } -core::matrix4SIMD CPropertyElementManager::retrieveMatrix(const std::string& _data, bool& success) +hlsl::float32_t4x4 CPropertyElementManager::retrieveMatrix(const std::string_view& _data, system::logger_opt_ptr logger) { - std::string str = _data; - std::replace(str.begin(), str.end(), ',', ' '); + std::string str(_data); + std::replace(str.begin(),str.end(),',',' '); - core::matrix4SIMD matrixData; + hlsl::float32_t4x4 matrixData; std::stringstream ss; ss << str; - for (auto i=0u; i<16u; i++) + for (auto r=0u; r<4u; r++) + for (auto c=0u; c<4u; c++) { float f = std::numeric_limits::quiet_NaN(); ss >> f; - if (isnan(f)) + if (core::isnan(f)) { - _NBL_DEBUG_BREAK_IF(true); - ParserLog::invalidXMLFileStructure("Invalid matrix specified."); - success = false; - return core::matrix4SIMD(); + invalidXMLFileStructure(logger,"Invalid matrix specified."); + matrixData[0][0] = f; + return matrixData; } - matrixData.pointer()[i] = f; + matrixData[r][c] = f; } return matrixData; } -core::vectorSIMDf CPropertyElementManager::retrieveVector(const std::string& _data, bool& success) +hlsl::float32_t4 CPropertyElementManager::retrieveVector(const std::string_view& _data, system::logger_opt_ptr logger) { - std::string str = _data; + std::string str(_data); std::replace(str.begin(), str.end(), ',', ' '); - float vectorData[4]; + hlsl::float32_t4 retval; std::stringstream ss; ss << str; @@ -323,55 +332,49 @@ core::vectorSIMDf CPropertyElementManager::retrieveVector(const std::string& _da float f = std::numeric_limits::quiet_NaN(); ss >> f; - vectorData[i] = f; + retval[i] = f; if (isnan(f)) { - if (i == 1) + if (i==1) // second not present { - vectorData[2] = vectorData[1] = vectorData[0]; - vectorData[3] = 0.0f; - break; + // make monochrome RGB or scalar XYZ + retval[2] = retval[1] = retval[0]; + retval[3] = 0.0f; } - else if (i == 3) + else if (i==3) // last not present { - vectorData[3] = 0.0f; - break; + // allow last coordinate to be 0 + retval[3] = 0.0f; } - success = false; - return core::vectorSIMDf(); + return retval; } } - return core::vectorSIMDf(vectorData); + return retval; } -core::vectorSIMDf CPropertyElementManager::retrieveHex(const std::string& _data, bool& success) +hlsl::float32_t4 CPropertyElementManager::retrieveHex(const std::string_view& _data, system::logger_opt_ptr logger) { - core::vectorSIMDf zero; auto ptr = _data.begin(); + const auto invalid = hlsl::float32_t4(std::numeric_limits::quiet_NaN()); + // not a hex if (_data.size()!=7u || *ptr!='#') - { - success = false; - return zero; - } + return invalid; - core::vectorSIMDf retval(0.f, 0.f, 0.f, 255.f); - for (auto i = 0; i < 3; i++) - for (auto j = 4; j >=0;j-=4) + hlsl::float32_t4 retval(0.f, 0.f, 0.f, 255.f); + for (auto i=0; i<3; i++) + for (auto j=4; j>=0;j-=4) { char c = *(++ptr); if (!isxdigit(c)) - { - success = false; - return zero; - } + return invalid; + // case insensitiveness int intval = (c >= 'A') ? (c - 'A' + 10) : (c - '0'); - retval[i] += float(intval < CArchiveLoaderZip::createArchive_impl(core::smart_refctd_ptr&& file, const std::string_view& password) const +core::smart_refctd_ptr CArchiveLoaderZip::createArchiveFromGZIP(core::smart_refctd_ptr&& file, const std::string_view& password) const { - if (!file) - return nullptr; - - uint16_t sig; - { - IFile::success_t success; - file->read(success,&sig,0,sizeof(sig)); - if (!success) - return nullptr; - } - std::shared_ptr> items = std::make_shared>(); core::vector itemsMetadata; - // load file entries - { - const bool isGZip = sig==0x8b1fu; - // - auto addItem = [&items,&itemsMetadata](const std::string& _path, const size_t offset, const SZIPFileHeader& meta) -> void - { - // we need to have a filename or we skip - if (_path.empty()) - return; - - auto& item = items->emplace_back(); - item.pathRelativeToArchive = _path; - item.size = meta.DataDescriptor.UncompressedSize; - item.offset = offset; - item.ID = itemsMetadata.size(); - item.allocatorType = meta.CompressionMethod ? IFileArchive::EAT_VIRTUAL_ALLOC:IFileArchive::EAT_NULL; - itemsMetadata.push_back(meta); - }; - - // - size_t offset = 0ull; - auto readStringFromFile = [&file,&offset](auto charCallback) -> bool + items->reserve(1u); + itemsMetadata.reserve(1u); + auto addItem = [&items, &itemsMetadata](const std::string& _path, const size_t offset, const SZIPFileHeader& meta) -> void + { + // we need to have a filename or we skip + if (_path.empty()) + return; + + auto& item = items->emplace_back(); + item.pathRelativeToArchive = _path; + item.size = meta.DataDescriptor.UncompressedSize; + item.offset = offset; + item.ID = itemsMetadata.size(); + item.allocatorType = meta.CompressionMethod ? IFileArchive::EAT_VIRTUAL_ALLOC : IFileArchive::EAT_NULL; + itemsMetadata.push_back(meta); + }; + + std::string filename; + size_t gzipFileOffset = 0ull; + auto readStringFromFile = [&file, &gzipFileOffset](auto charCallback) -> bool { char c = 0x45; // make sure we start with non-zero char while (c) { IFile::success_t success; - file->read(success,&c,offset,sizeof(c)); + file->read(success, &c, gzipFileOffset, sizeof(c)); if (!success) return false; - offset += success.getBytesToProcess(); + gzipFileOffset += success.getBytesToProcess(); charCallback(c); } // if string is not null terminated, something went wrong reading the file return !c; }; - - // - std::string filename; - filename.reserve(ISystem::MAX_FILENAME_LENGTH); - if (isGZip) + + SGZIPMemberHeader gzipHeader; + { + IFile::success_t success; + file->read(success, &gzipHeader, gzipFileOffset, sizeof(gzipHeader)); + if (!success) + return nullptr; + gzipFileOffset += sizeof(gzipHeader); + } + + //! The gzip file format seems to think that there can be multiple files in a gzip file + //! TODO: But OLD Irrlicht Impl doesn't honor it!? + if (gzipHeader.sig != SGZIPMemberHeader::ExpectedSignature) + return nullptr; + + // now get the file info + if (gzipHeader.flags & EGZF_EXTRA_FIELDS) + { + // read lenth of extra data + uint16_t dataLen; + IFile::success_t success; + file->read(success, &dataLen, gzipFileOffset, sizeof(dataLen)); + if (!success) + return nullptr; + gzipFileOffset += success.getBytesToProcess(); + // skip the extra data + gzipFileOffset += dataLen; + } + // + if (gzipHeader.flags & EGZF_FILE_NAME) + { + filename.clear(); + if (!readStringFromFile([&](const char c) {filename.push_back(c); })) + return nullptr; + } + // + if (gzipHeader.flags & EGZF_COMMENT) + { + if (!readStringFromFile([](const char c) {})) + return nullptr; + } + // skip crc16 + if (gzipHeader.flags & EGZF_CRC16) + gzipFileOffset += 2; + + + SZIPFileHeader header{}; + header.FilenameLength = filename.length(); + header.CompressionMethod = gzipHeader.compressionMethod; + header.DataDescriptor.CompressedSize = file->getSize() - (gzipFileOffset + sizeof(uint64_t)); + + const size_t itemOffset = gzipFileOffset; + + gzipFileOffset += header.DataDescriptor.CompressedSize; + // read CRC + { + IFile::success_t success; + file->read(success, &header.DataDescriptor.CRC32, gzipFileOffset, sizeof(header.DataDescriptor.CRC32)); + if (!success) + return nullptr; + gzipFileOffset += success.getBytesToProcess(); + } + // read uncompressed size + { + IFile::success_t success; + file->read(success, &header.DataDescriptor.UncompressedSize, gzipFileOffset, sizeof(header.DataDescriptor.UncompressedSize)); + if (!success) + return nullptr; + gzipFileOffset += success.getBytesToProcess(); + } + + // + addItem(filename, itemOffset, header); + + assert(items->size() == itemsMetadata.size()); + if (items->empty()) + return nullptr; + + return core::make_smart_refctd_ptr(std::move(file), core::smart_refctd_ptr(m_logger.get()), items, std::move(itemsMetadata)); +} +core::smart_refctd_ptr CArchiveLoaderZip::createArchiveFromZIP(core::smart_refctd_ptr&& file, const std::string_view& password) const +{ + const size_t fileSize = file->getSize(); + if (fileSize < sizeof(SZIPFileCentralDirEnd)) + return nullptr; + + SZIPFileCentralDirEnd dirEnd; + { + dirEnd.Sig = 0u; + constexpr size_t kMaxZipCommentSize = 0xffffu; + size_t endOfCentralDirectoryOffset = fileSize - sizeof(SZIPFileCentralDirEnd); + const size_t minEndOffset = (fileSize > sizeof(SZIPFileCentralDirEnd) + kMaxZipCommentSize) ? (fileSize - sizeof(SZIPFileCentralDirEnd) - kMaxZipCommentSize) : 0u; + bool found = false; + while (true) { - SGZIPMemberHeader gzipHeader; + IFile::success_t success; + file->read(success, &dirEnd, endOfCentralDirectoryOffset, sizeof(dirEnd)); + if (success && dirEnd.Sig == SZIPFileCentralDirEnd::ExpectedSig) { - IFile::success_t success; - file->read(success,&gzipHeader,0ull,sizeof(gzipHeader)); - if (!success) - return nullptr; - offset += success.getBytesToProcess(); + found = true; + break; } + if (endOfCentralDirectoryOffset == minEndOffset) + break; + --endOfCentralDirectoryOffset; + } + if (!found) + return nullptr; + } + + // multiple disks are not supported + if (dirEnd.NumberDisk != 0 || dirEnd.NumberStart != 0) + { + assert(false); + return nullptr; + } + if (dirEnd.Offset > fileSize || dirEnd.Size > fileSize - dirEnd.Offset) + return nullptr; - //! The gzip file format seems to think that there can be multiple files in a gzip file - //! TODO: But OLD Irrlicht Impl doesn't honor it!? - if (gzipHeader.sig!=0x8b1fu) + std::shared_ptr> items = std::make_shared>(); + core::vector itemsMetadata; + + items->reserve(dirEnd.TotalEntries); + itemsMetadata.reserve(dirEnd.TotalEntries); + auto addItem = [&items, &itemsMetadata](const std::string& _path, const size_t offset, const SZIPFileHeader& meta) -> void + { + // we need to have a filename or we skip + if (_path.empty()) + return; + + auto& item = items->emplace_back(); + item.pathRelativeToArchive = _path; + item.size = meta.DataDescriptor.UncompressedSize; + item.offset = offset; + item.ID = itemsMetadata.size(); + item.allocatorType = meta.CompressionMethod ? IFileArchive::EAT_VIRTUAL_ALLOC : IFileArchive::EAT_NULL; + itemsMetadata.push_back(meta); + }; + + size_t centralDirectoryOffset = dirEnd.Offset; + for (int i = 0; i < dirEnd.TotalEntries; ++i) + { + SZIPFileCentralDirFileHeader centralDirectoryHeader; + { + if (centralDirectoryOffset > fileSize || fileSize - centralDirectoryOffset < sizeof(SZIPFileCentralDirFileHeader)) return nullptr; - - // now get the file info - if (gzipHeader.flags&EGZF_EXTRA_FIELDS) - { - // read lenth of extra data - uint16_t dataLen; - IFile::success_t success; - file->read(success,&dataLen,offset,sizeof(dataLen)); - if (!success) - return nullptr; - offset += success.getBytesToProcess(); - // skip the extra data - offset += dataLen; - } - // - if (gzipHeader.flags&EGZF_FILE_NAME) - { - filename.clear(); - if (!readStringFromFile([&](const char c){filename.push_back(c);})) - return nullptr; - } - // - if (gzipHeader.flags&EGZF_COMMENT) - { - if (!readStringFromFile([](const char c){})) - return nullptr; - } - // skip crc16 - if (gzipHeader.flags&EGZF_CRC16) - offset += 2; + IFile::success_t success; + file->read(success, ¢ralDirectoryHeader, centralDirectoryOffset, sizeof(SZIPFileCentralDirFileHeader)); + if (!success) + return nullptr; + } + if (centralDirectoryHeader.Sig != SZIPFileCentralDirFileHeader::ExpectedSignature) + { + // .zip file is corrupted + assert(false); + return nullptr; + } - SZIPFileHeader header; - memset(&header,0,sizeof(SZIPFileHeader)); - header.FilenameLength = filename.length(); - header.CompressionMethod = gzipHeader.compressionMethod; - header.DataDescriptor.CompressedSize = file->getSize()-(offset+sizeof(uint64_t)); + const size_t centralHeaderSize = centralDirectoryHeader.calcSize(); + if (centralHeaderSize < sizeof(SZIPFileCentralDirFileHeader)) + return nullptr; + if (centralDirectoryOffset + centralHeaderSize > fileSize) + return nullptr; + centralDirectoryOffset += centralHeaderSize; - const size_t itemOffset = offset; - - offset += header.DataDescriptor.CompressedSize; - // read CRC - { - IFile::success_t success; - file->read(success,&header.DataDescriptor.CRC32,offset,sizeof(header.DataDescriptor.CRC32)); - if (!success) - return nullptr; - offset += success.getBytesToProcess(); - } - // read uncompressed size - { - IFile::success_t success; - file->read(success,&header.DataDescriptor.UncompressedSize,offset,sizeof(header.DataDescriptor.UncompressedSize)); - if (!success) - return nullptr; - offset += success.getBytesToProcess(); - } + SZIPFileHeader localFileHeader; + { + const size_t localHeaderOffset = centralDirectoryHeader.RelativeOffsetOfLocalHeader; + if (localHeaderOffset > fileSize || fileSize - localHeaderOffset < sizeof(SZIPFileHeader)) + return nullptr; + IFile::success_t success; + file->read(success, &localFileHeader, localHeaderOffset, sizeof(SZIPFileHeader)); + if (!success) + return nullptr; + } + if (localFileHeader.Sig != SZIPFileHeader::ExpectedSignature) + return nullptr; + const size_t localHeaderSize = localFileHeader.calcSize(); + if (localHeaderSize < sizeof(SZIPFileHeader)) + return nullptr; + if (centralDirectoryHeader.RelativeOffsetOfLocalHeader + localHeaderSize > fileSize) + return nullptr; - // - addItem(filename,itemOffset,header); + std::string filename; + filename.resize(localFileHeader.FilenameLength); + { + IFile::success_t success; + const size_t filenameOffset = centralDirectoryHeader.RelativeOffsetOfLocalHeader + sizeof(SZIPFileHeader); + file->read(success, filename.data(), filenameOffset, localFileHeader.FilenameLength); + // TODO: assertion + if (!success) + return nullptr; } - else + + // AES encryption + if ((localFileHeader.GeneralBitFlag & ZIP_FILE_ENCRYPTED) && (localFileHeader.CompressionMethod == 99)) { - while (true) + SZipFileExtraHeader extraHeader; + SZipFileAESExtraData data; + + size_t localOffset = centralDirectoryHeader.RelativeOffsetOfLocalHeader + sizeof(SZIPFileHeader) + localFileHeader.FilenameLength; + size_t offset = localOffset + localFileHeader.ExtraFieldLength; + while (localOffset + sizeof(extraHeader) <= offset) { - SZIPFileHeader zipHeader; { IFile::success_t success; - file->read(success,&zipHeader,offset,sizeof(zipHeader)); + file->read(success, &extraHeader, localOffset, sizeof(extraHeader)); if (!success) break; - offset += success.getBytesToProcess(); + localOffset += sizeof(extraHeader); } - if (zipHeader.Sig!=0x04034b50u) + if (extraHeader.Size < 0) + break; + const size_t extraSize = static_cast(extraHeader.Size); + if (extraSize == 0) + break; + if (localOffset + extraSize > offset) break; - filename.resize(zipHeader.FilenameLength); + if (extraHeader.ID != 0x9901u) { - IFile::success_t success; - file->read(success,filename.data(),offset,zipHeader.FilenameLength); - if (!success) - break; - offset += success.getBytesToProcess(); + localOffset += extraSize; + continue; } - // AES encryption - if ((zipHeader.GeneralBitFlag&ZIP_FILE_ENCRYPTED) && (zipHeader.CompressionMethod==99)) + if (extraSize < sizeof(SZipFileAESExtraData)) + break; { - SZipFileExtraHeader extraHeader; - SZipFileAESExtraData data; - - size_t localOffset = offset; - offset += zipHeader.ExtraFieldLength; - while (true) - { - { - IFile::success_t success; - file->read(success,&extraHeader,localOffset,sizeof(extraHeader)); - if (!success) - break; - localOffset += success.getBytesToProcess(); - if (localOffset>offset) - break; - } - - if (extraHeader.ID!=0x9901u) - continue; - - { - IFile::success_t success; - file->read(success,&data,localOffset,sizeof(data)); - if (!success) - break; - localOffset += success.getBytesToProcess(); - if (localOffset>offset) - break; - } - if (data.Vendor[0]=='A' && data.Vendor[1]=='E') - { - #ifdef _NBL_COMPILE_WITH_ZIP_ENCRYPTION_ - // encode values into Sig - // AE-Version | Strength | ActualMode - zipHeader.Sig = - ((data.Version & 0xff) << 24) | - (data.EncryptionStrength << 16) | - (data.CompressionMode); - break; - #else - filename.clear(); // no support, can't decrypt - #endif - } - } + IFile::success_t success; + file->read(success, &data, localOffset, sizeof(data)); + if (!success) + break; } - else - offset += zipHeader.ExtraFieldLength; - - // if bit 3 was set, use CentralDirectory for setup - if (zipHeader.GeneralBitFlag&ZIP_INFO_IN_DATA_DESCRIPTOR) + if (data.Vendor[0] == 'A' && data.Vendor[1] == 'E') { - SZIPFileCentralDirEnd dirEnd; - dirEnd.Sig = 0u; - - // First place where the end record could be stored - offset = file->getSize()-sizeof(SZIPFileCentralDirEnd)+1ull; - while (dirEnd.Sig!=SZIPFileCentralDirEnd::ExpectedSig) - { - IFile::success_t success; - file->read(success,&dirEnd,--offset,sizeof(dirEnd)); - if (!success) - return nullptr; - } - items->reserve(dirEnd.TotalEntries); - itemsMetadata.reserve(dirEnd.TotalEntries); - offset = dirEnd.Offset; - #if 0 - while (scanCentralDirectoryHeader(offset)) {} - #endif - assert(false); // if you ever hit this, msg @devsh +#ifdef _NBL_COMPILE_WITH_ZIP_ENCRYPTION_ + // encode values into Sig + // AE-Version | Strength | ActualMode + localFileHeader.Sig = + ((data.Version & 0xff) << 24) | + (data.EncryptionStrength << 16) | + (data.CompressionMode); break; +#else + filename.clear(); // no support, can't decrypt +#endif } - - addItem(filename,offset,zipHeader); - // move forward length of data - offset += zipHeader.DataDescriptor.CompressedSize; + localOffset += extraSize; } } + + // copying the data descriptor from the central directory header because it is always valid (data descriptor from local file header may be invalid when bit 3 in general purpose bit flag is set) + localFileHeader.DataDescriptor = centralDirectoryHeader.DataDescriptor; + + const size_t fileDataOffset = centralDirectoryHeader.RelativeOffsetOfLocalHeader + localFileHeader.calcSize(); + addItem(filename, fileDataOffset, localFileHeader); } - assert(items->size()==itemsMetadata.size()); + assert(items->size() == itemsMetadata.size()); if (items->empty()) return nullptr; - return core::make_smart_refctd_ptr(std::move(file),core::smart_refctd_ptr(m_logger.get()), items, std::move(itemsMetadata)); + return core::make_smart_refctd_ptr(std::move(file), core::smart_refctd_ptr(m_logger.get()), items, std::move(itemsMetadata)); } -#if 0 -bool CFileArchiveZip::scanCentralDirectoryHeader(size_t& offset) +core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core::smart_refctd_ptr&& file, const std::string_view& password) const { - std::filesystem::path ZipFileName = ""; - SZIPFileCentralDirFileHeader entry; + if (!file) + return nullptr; + + uint16_t sig; + IFile::success_t success; + file->read(success, &sig, 0, sizeof(sig)); + if (!success) + return nullptr; + + const bool isGZIP = sig == SGZIPMemberHeader::ExpectedSignature; + if (isGZIP) { - system::future fut; - m_file->read(fut, &entry, offset, sizeof(SZIPFileCentralDirFileHeader)); - fut.get(); - offset += sizeof(SZIPFileCentralDirFileHeader); + return createArchiveFromGZIP(std::move(file), password); + } + else + { + return createArchiveFromZIP(std::move(file), password); } - - if (entry.Sig != 0x02014b50) - return false; // central dir headers end here. - - const long pos = offset; - offset = entry.RelativeOffsetOfLocalHeader; - scanZipHeader(offset, true); - offset = pos + entry.FilenameLength + entry.ExtraFieldLength + entry.FileCommentLength; - m_fileInfo.back().header.DataDescriptor.CompressedSize = entry.CompressedSize; - m_fileInfo.back().header.DataDescriptor.UncompressedSize = entry.UncompressedSize; - m_fileInfo.back().header.DataDescriptor.CRC32 = entry.CRC32; - m_files.back().size = entry.UncompressedSize; - return true; } -#endif CFileArchive::file_buffer_t CArchiveLoaderZip::CArchive::getFileBuffer(const IFileArchive::SFileList::found_t& item) { @@ -620,4 +684,4 @@ namespace void SzFree(void *p, void *address) { p = p; _NBL_ALIGNED_FREE(address); } ISzAlloc lzmaAlloc = { SzAlloc, SzFree }; } -#endif \ No newline at end of file +#endif diff --git a/src/nbl/system/CArchiveLoaderZip.h b/src/nbl/system/CArchiveLoaderZip.h index 0a57b62ec9..1524bbe22d 100644 --- a/src/nbl/system/CArchiveLoaderZip.h +++ b/src/nbl/system/CArchiveLoaderZip.h @@ -31,8 +31,18 @@ class CArchiveLoaderZip final : public IArchiveLoader int16_t ExtraFieldLength; // filename (variable size) // extra field (variable size ) + + static constexpr uint32_t ExpectedSignature = 0x04034b50u; + + size_t calcSize() const + { + return sizeof(SZIPFileHeader) + FilenameLength + ExtraFieldLength; + } } PACK_STRUCT; #include "nbl/nblunpack.h" + + static_assert(sizeof(SZIPFileHeader) == 30); + class CArchive final : public CFileArchive { public: @@ -75,6 +85,8 @@ class CArchiveLoaderZip final : public IArchiveLoader private: core::smart_refctd_ptr createArchive_impl(core::smart_refctd_ptr&& file, const std::string_view& password) const override; + core::smart_refctd_ptr createArchiveFromGZIP(core::smart_refctd_ptr&& file, const std::string_view& password) const; + core::smart_refctd_ptr createArchiveFromZIP(core::smart_refctd_ptr&& file, const std::string_view& password) const; }; } diff --git a/src/nbl/system/CColoredStdoutLoggerWin32.cpp b/src/nbl/system/CColoredStdoutLoggerWin32.cpp index e664ae84bc..f2690a81b4 100644 --- a/src/nbl/system/CColoredStdoutLoggerWin32.cpp +++ b/src/nbl/system/CColoredStdoutLoggerWin32.cpp @@ -15,7 +15,7 @@ CColoredStdoutLoggerWin32::CColoredStdoutLoggerWin32(core::bitflag void CColoredStdoutLoggerWin32::threadsafeLog_impl(const std::string_view& fmt, E_LOG_LEVEL logLevel, va_list args) { SetConsoleTextAttribute(m_native_console, getConsoleColor(logLevel)); - printf(constructLogString(fmt, logLevel, args).data()); + printf("%s", constructLogString(fmt, logLevel, args).data()); fflush(stdout); SetConsoleTextAttribute(m_native_console, 15); // restore to white } diff --git a/src/nbl/system/CSystemWin32.cpp b/src/nbl/system/CSystemWin32.cpp index cab809c145..2798b4fb27 100644 --- a/src/nbl/system/CSystemWin32.cpp +++ b/src/nbl/system/CSystemWin32.cpp @@ -43,6 +43,11 @@ core::smart_refctd_ptr CSystemWin32::CCaller::createFile(const std: { const bool writeAccess = flags.value&IFile::ECF_WRITE; const DWORD fileAccess = ((flags.value&IFile::ECF_READ) ? FILE_GENERIC_READ:0)|(writeAccess ? FILE_GENERIC_WRITE:0); + DWORD shareMode = FILE_SHARE_READ; + if (flags.value & IFile::ECF_SHARE_READ_WRITE) + shareMode |= FILE_SHARE_WRITE; + if (flags.value & IFile::ECF_SHARE_DELETE) + shareMode |= FILE_SHARE_DELETE; SECURITY_ATTRIBUTES secAttribs{ sizeof(SECURITY_ATTRIBUTES), nullptr, FALSE }; @@ -51,8 +56,8 @@ core::smart_refctd_ptr CSystemWin32::CCaller::createFile(const std: p.make_preferred(); // Replace "/" separators with "\" // only write access should create new files if they don't exist - const auto creationDisposition = writeAccess ? OPEN_ALWAYS:OPEN_EXISTING; - HANDLE _native = CreateFileA(p.string().data(), fileAccess, FILE_SHARE_READ, &secAttribs, creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr); + const auto creationDisposition = writeAccess ? OPEN_ALWAYS : OPEN_EXISTING; + HANDLE _native = CreateFileA(p.string().data(), fileAccess, shareMode, &secAttribs, creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr); if (_native==INVALID_HANDLE_VALUE) { auto e = GetLastError(); @@ -107,4 +112,4 @@ bool isDebuggerAttached() return IsDebuggerPresent(); } -#endif \ No newline at end of file +#endif diff --git a/src/nbl/video/CVulkanCommandBuffer.cpp b/src/nbl/video/CVulkanCommandBuffer.cpp index a55c3a1e7b..a04b5940ce 100644 --- a/src/nbl/video/CVulkanCommandBuffer.cpp +++ b/src/nbl/video/CVulkanCommandBuffer.cpp @@ -844,17 +844,12 @@ bool CVulkanCommandBuffer::setRayTracingPipelineStackSize_impl(uint32_t pipeline return true; } -bool CVulkanCommandBuffer::traceRays_impl( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth) -{ - const auto vk_raygenGroupRegion = getVkStridedDeviceAddressRegion(raygenGroupRange, raygenGroupRange.size); - const auto vk_missGroupsRegion = getVkStridedDeviceAddressRegion(missGroupsRange, missGroupStride); - const auto vk_hitGroupsRegion = getVkStridedDeviceAddressRegion(hitGroupsRange, hitGroupStride); - const auto vk_callableGroupsRegion = getVkStridedDeviceAddressRegion(callableGroupsRange, callableGroupStride); +bool CVulkanCommandBuffer::traceRays_impl(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth) +{ + const auto vk_raygenGroupRegion = getVkStridedDeviceAddressRegion({.range=sbt.raygen,.stride=uint32_t(sbt.raygen.size)}); + const auto vk_missGroupsRegion = getVkStridedDeviceAddressRegion(sbt.miss); + const auto vk_hitGroupsRegion = getVkStridedDeviceAddressRegion(sbt.hit); + const auto vk_callableGroupsRegion = getVkStridedDeviceAddressRegion(sbt.callable); getFunctionTable().vkCmdTraceRaysKHR(m_cmdbuf, &vk_raygenGroupRegion, diff --git a/src/nbl/video/CVulkanCommandBuffer.h b/src/nbl/video/CVulkanCommandBuffer.h index 9383585b23..48d7e9e85c 100644 --- a/src/nbl/video/CVulkanCommandBuffer.h +++ b/src/nbl/video/CVulkanCommandBuffer.h @@ -226,12 +226,7 @@ class CVulkanCommandBuffer final : public IGPUCommandBuffer bool resolveImage_impl(const IGPUImage* const srcImage, const IGPUImage::LAYOUT srcImageLayout, IGPUImage* const dstImage, const IGPUImage::LAYOUT dstImageLayout, const uint32_t regionCount, const SImageResolve* pRegions) override; bool setRayTracingPipelineStackSize_impl(uint32_t pipelineStackSize) override; - bool traceRays_impl( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth) override; + bool traceRays_impl(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth) override; bool traceRaysIndirect_impl(const asset::SBufferBinding& indirectBinding) override; bool executeCommands_impl(const uint32_t count, IGPUCommandBuffer* const* const cmdbufs) override; diff --git a/src/nbl/video/CVulkanCommandPool.h b/src/nbl/video/CVulkanCommandPool.h index 8775553b99..d1c3085b4e 100644 --- a/src/nbl/video/CVulkanCommandPool.h +++ b/src/nbl/video/CVulkanCommandPool.h @@ -2,7 +2,6 @@ #define _NBL_VIDEO_C_VULKAN_COMMAND_POOL_H_INCLUDED_ #include "nbl/video/IGPUCommandPool.h" -#include "nbl/core/containers/CMemoryPool.h" #include diff --git a/src/nbl/video/CVulkanLogicalDevice.cpp b/src/nbl/video/CVulkanLogicalDevice.cpp index 5390b4c3fa..46b79a7094 100644 --- a/src/nbl/video/CVulkanLogicalDevice.cpp +++ b/src/nbl/video/CVulkanLogicalDevice.cpp @@ -1551,7 +1551,6 @@ void CVulkanLogicalDevice::createRayTracingPipelines_impl( for (const auto& info : createInfos) { - core::unordered_map shaderIndexes; auto getVkShaderIndex = [&](const IGPUPipelineBase::SShaderSpecInfo& spec) { @@ -1633,6 +1632,7 @@ void CVulkanLogicalDevice::createRayTracingPipelines_impl( { outCreateInfo->pDynamicState = &vk_dynamicStateCreateInfo; } + outCreateInfo++; } auto vk_pipelines = reinterpret_cast(output); diff --git a/src/nbl/video/IGPUCommandBuffer.cpp b/src/nbl/video/IGPUCommandBuffer.cpp index 1f619666ab..f47428aae8 100644 --- a/src/nbl/video/IGPUCommandBuffer.cpp +++ b/src/nbl/video/IGPUCommandBuffer.cpp @@ -50,14 +50,6 @@ bool IGPUCommandBuffer::checkStateBeforeRecording(const core::bitflag flags, const SInheritanceInfo* inheritanceInfo) { - // Using Vulkan 1.2 VUIDs here because we don't want to confuse ourselves with Dynamic Rendering being core - // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkBeginCommandBuffer-commandBuffer-00049 - if (m_state == STATE::RECORDING || m_state == STATE::PENDING) - { - NBL_LOG_ERROR("command buffer must not be in RECORDING or PENDING state!"); - return false; - } - const bool whollyInsideRenderpass = flags.hasFlags(USAGE::RENDER_PASS_CONTINUE_BIT); const auto physDev = getOriginDevice()->getPhysicalDevice(); if (m_level==IGPUCommandPool::BUFFER_LEVEL::PRIMARY) @@ -126,6 +118,14 @@ bool IGPUCommandBuffer::begin(const core::bitflag flags, const SInheritan } checkForParentPoolReset(); + + // Using Vulkan 1.2 VUIDs here because we don't want to confuse ourselves with Dynamic Rendering being core + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-vkBeginCommandBuffer-commandBuffer-00049 + if (m_state == STATE::RECORDING || m_state == STATE::PENDING) + { + NBL_LOG_ERROR("command buffer must not be in RECORDING or PENDING state!"); + return false; + } // still not initial and pool wasn't reset if (m_state!=STATE::INITIAL) @@ -684,50 +684,21 @@ bool IGPUCommandBuffer::copyImage(const IGPUImage* const srcImage, const IGPUIma return copyImage_impl(srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); } -bool IGPUCommandBuffer::invalidShaderGroups( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - core::bitflag flags) const +bool IGPUCommandBuffer::invalidShaderGroups(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const core::bitflag flags) const { + if (!sbt.valid(flags)) + return true; using PipelineFlag = IGPURayTracingPipeline::SCreationParams::FLAGS; using PipelineFlags = core::bitflag; - // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03696 - // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03697 - // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03512 - const auto shouldHaveHitGroup = flags & - (PipelineFlags(PipelineFlag::NO_NULL_ANY_HIT_SHADERS) | - PipelineFlag::NO_NULL_CLOSEST_HIT_SHADERS | - PipelineFlag::NO_NULL_INTERSECTION_SHADERS); - if (shouldHaveHitGroup && !hitGroupsRange.buffer) - { - NBL_LOG_ERROR("bound pipeline indicates that traceRays command should have hit group, but hitGroupsRange.buffer is null!"); - return true; - } - - // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-flags-03511 - const auto shouldHaveMissGroup = flags & PipelineFlag::NO_NULL_MISS_SHADERS; - if (shouldHaveMissGroup && !missGroupsRange.buffer) - { - NBL_LOG_ERROR("bound pipeline indicates that traceRays command should have hit group, but hitGroupsRange.buffer is null!"); - return true; - } - const auto& limits = getOriginDevice()->getPhysicalDevice()->getLimits(); - auto invalidBufferRegion = [this, &limits](const asset::SBufferRange& range, uint32_t stride, const char* groupName) -> bool + auto invalidBufferRegion = [this, &limits](const asset::SStridedRange& stRange, const char* groupName) -> bool { + const auto& range = stRange.range; const IGPUBuffer* const buffer = range.buffer.get(); - - if (!buffer) return false; - - if (!range.isValid()) - { - NBL_LOG_ERROR("%s buffer range is not valid!", groupName); - return true; - } + if (!buffer) + return false; if (!(buffer->getCreationParams().usage & IGPUBuffer::EUF_SHADER_DEVICE_ADDRESS_BIT)) { @@ -743,13 +714,13 @@ bool IGPUCommandBuffer::invalidShaderGroups( } // https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-pHitShaderBindingTable-03690 - if (stride % limits.shaderGroupHandleAlignment) + if (stRange.stride % limits.shaderGroupHandleAlignment) { NBL_LOG_ERROR("%s buffer offset must be multiple of %u!", groupName, limits.shaderGroupHandleAlignment); return true; } - if (stride > limits.maxShaderGroupStride) + if (stRange.stride > limits.maxShaderGroupStride) { NBL_LOG_ERROR("%s buffer stride must not exceed %u!", groupName, limits.shaderGroupHandleAlignment); return true; @@ -765,10 +736,11 @@ bool IGPUCommandBuffer::invalidShaderGroups( return false; }; - if (invalidBufferRegion(raygenGroupRange, raygenGroupRange.size, "Raygen Group")) return true; - if (invalidBufferRegion(missGroupsRange, missGroupStride, "Miss groups")) return true; - if (invalidBufferRegion(hitGroupsRange, hitGroupStride, "Hit groups")) return true; - if (invalidBufferRegion(callableGroupsRange, callableGroupStride, "Callable groups")) return true; + if (invalidBufferRegion({.range=sbt.raygen,.stride=limits.shaderGroupHandleAlignment},"Raygen Group")) return true; + if (invalidBufferRegion(sbt.miss,"Miss groups")) return true; + if (invalidBufferRegion(sbt.hit,"Hit groups")) return true; + if (invalidBufferRegion(sbt.callable,"Callable groups")) return true; + return false; } @@ -1945,12 +1917,7 @@ bool IGPUCommandBuffer::setRayTracingPipelineStackSize(uint32_t pipelineStackSiz return setRayTracingPipelineStackSize_impl(pipelineStackSize); } -bool IGPUCommandBuffer::traceRays( - const asset::SBufferRange& raygenGroupRange, - const asset::SBufferRange& missGroupsRange, uint32_t missGroupStride, - const asset::SBufferRange& hitGroupsRange, uint32_t hitGroupStride, - const asset::SBufferRange& callableGroupsRange, uint32_t callableGroupStride, - uint32_t width, uint32_t height, uint32_t depth) +bool IGPUCommandBuffer::traceRays(const IGPURayTracingPipeline::SShaderBindingTable& sbt, const uint32_t width, const uint32_t height, const uint32_t depth) { if (!checkStateBeforeRecording(queue_flags_t::COMPUTE_BIT,RENDERPASS_SCOPE::OUTSIDE)) return false; @@ -1983,11 +1950,7 @@ bool IGPUCommandBuffer::traceRays( } const auto flags = m_boundRayTracingPipeline->getCreationFlags(); - if (invalidShaderGroups(raygenGroupRange, - missGroupsRange, missGroupStride, - hitGroupsRange, hitGroupStride, - callableGroupsRange, callableGroupStride, - flags)) + if (invalidShaderGroups(sbt,flags)) { NBL_LOG_ERROR("invalid shader groups for traceRays command!"); return false; @@ -2000,11 +1963,13 @@ bool IGPUCommandBuffer::traceRays( return false; } - if (!m_cmdpool->m_commandListPool.emplace(m_commandList, - core::smart_refctd_ptr(raygenGroupRange.buffer), - core::smart_refctd_ptr(missGroupsRange.buffer), - core::smart_refctd_ptr(hitGroupsRange.buffer), - core::smart_refctd_ptr(callableGroupsRange.buffer))) + if (!m_cmdpool->m_commandListPool.emplace( + m_commandList, + core::smart_refctd_ptr(sbt.raygen.buffer), + core::smart_refctd_ptr(sbt.miss.range.buffer), + core::smart_refctd_ptr(sbt.hit.range.buffer), + core::smart_refctd_ptr(sbt.callable.range.buffer) + )) { NBL_LOG_ERROR("out of host memory!"); return false; @@ -2012,12 +1977,7 @@ bool IGPUCommandBuffer::traceRays( m_noCommands = false; - return traceRays_impl( - raygenGroupRange, - missGroupsRange, missGroupStride, - hitGroupsRange, hitGroupStride, - callableGroupsRange, callableGroupStride, - width, height, depth); + return traceRays_impl(sbt, width, height, depth); } bool IGPUCommandBuffer::traceRaysIndirect(const asset::SBufferBinding& indirectBinding) diff --git a/src/nbl/video/ILogicalDevice.cpp b/src/nbl/video/ILogicalDevice.cpp index 7c3f5dbb81..d5b38f9b69 100644 --- a/src/nbl/video/ILogicalDevice.cpp +++ b/src/nbl/video/ILogicalDevice.cpp @@ -1110,14 +1110,14 @@ bool ILogicalDevice::createRayTracingPipelines(IGPUPipelineCache* const pipeline newParams[ix] = param; newParams[ix].shaderGroups.raygen = trimTask.trim(param.shaderGroups.raygen, trimmedShaders); - newParams[ix].shaderGroups.misses = trimmedMissSpecs; + newParams[ix].shaderGroups.misses = {trimmedMissSpecData,param.shaderGroups.misses.size()}; for (const auto& miss: param.shaderGroups.misses) { *trimmedMissSpecData = trimTask.trim(miss, trimmedShaders); trimmedMissSpecData++; } - newParams[ix].shaderGroups.hits = trimmedHitSpecs; + newParams[ix].shaderGroups.hits = {trimmedHitSpecData,param.shaderGroups.hits.size()}; for (const auto& hit: param.shaderGroups.hits) { *trimmedHitSpecData = { @@ -1128,7 +1128,7 @@ bool ILogicalDevice::createRayTracingPipelines(IGPUPipelineCache* const pipeline trimmedHitSpecData++; } - newParams[ix].shaderGroups.callables = trimmedCallableSpecs; + newParams[ix].shaderGroups.callables = {trimmedCallableSpecData,param.shaderGroups.callables.size()}; for (const auto& callable: param.shaderGroups.callables) { *trimmedCallableSpecData = trimTask.trim(callable, trimmedShaders); diff --git a/src/nbl/video/utilities/CAssetConverter.cpp b/src/nbl/video/utilities/CAssetConverter.cpp index 69346a4049..d7f2d7dbbc 100644 --- a/src/nbl/video/utilities/CAssetConverter.cpp +++ b/src/nbl/video/utilities/CAssetConverter.cpp @@ -2480,7 +2480,7 @@ class MetaDeviceMemoryAllocator final // bind everything for (auto i=0; i CAssetConverter::convert_impl(SReserveResul } #endif } -} \ No newline at end of file +} diff --git a/tools/nsc/CMakeLists.txt b/tools/nsc/CMakeLists.txt index bcdcbca531..2765f02fa5 100644 --- a/tools/nsc/CMakeLists.txt +++ b/tools/nsc/CMakeLists.txt @@ -2,6 +2,9 @@ nbl_create_executable_project("" "" "" "") enable_testing() +add_dependencies(${EXECUTABLE_NAME} argparse) +target_include_directories(${EXECUTABLE_NAME} PRIVATE $) + set(GODBOLT_BINARY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/compiler-explorer") set(GODBOLT_BINARY_PRETEST_DIRECTORY "${GODBOLT_BINARY_DIRECTORY}/pre-test") set(NBL_NSC_COMPILE_DIRECTORY "${GODBOLT_BINARY_PRETEST_DIRECTORY}/.compile/$") @@ -359,4 +362,4 @@ add_custom_target(run-compiler-explorer ALL add_dependencies(run-compiler-explorer nsc) set_target_properties(run-compiler-explorer PROPERTIES FOLDER "Godbolt") -endif() \ No newline at end of file +endif() diff --git a/tools/nsc/main.cpp b/tools/nsc/main.cpp index c4ce43b326..64ad684b0c 100644 --- a/tools/nsc/main.cpp +++ b/tools/nsc/main.cpp @@ -1,365 +1,597 @@ #include "nabla.h" #include "nbl/system/IApplicationFramework.h" - #include #include +#include #include #include - +#include +#include +#include +#include +#include +#include +#include +#include #include "nbl/asset/metadata/CHLSLMetadata.h" #include "nlohmann/json.hpp" -using json = nlohmann::json; +using json = nlohmann::json; using namespace nbl; using namespace nbl::system; using namespace nbl::core; using namespace nbl::asset; -class ShaderCompiler final : public system::IApplicationFramework +class TrimStdoutLogger final : public CStdoutLogger { - using base_t = system::IApplicationFramework; - public: - using base_t::base_t; - - bool onAppInitialized(smart_refctd_ptr&& system) override - { - const auto argc = argv.size(); - const bool insufficientArguments = argc < 2; - - if (not insufficientArguments) - { - // 1) NOTE: imo each example should be able to dump build info & have such mode, maybe it could go straight to IApplicationFramework main - // 2) TODO: this whole "serialize" logic should go to the GitInfo struct and be static or something, it should be standardized - - if (argv[1] == "--dump-build-info") - { - json j; - - auto& modules = j["modules"]; - - auto serialize = [&](const gtml::GitInfo& info, std::string_view target) -> void - { - auto& s = modules[target.data()]; - - s["isPopulated"] = info.isPopulated; - if (info.hasUncommittedChanges.has_value()) - s["hasUncommittedChanges"] = info.hasUncommittedChanges.value(); - else - s["hasUncommittedChanges"] = "UNKNOWN, BUILT WITHOUT DIRTY-CHANGES CAPTURE"; - - s["commitAuthorName"] = info.commitAuthorName; - s["commitAuthorEmail"] = info.commitAuthorEmail; - s["commitHash"] = info.commitHash; - s["commitShortHash"] = info.commitShortHash; - s["commitDate"] = info.commitDate; - s["commitSubject"] = info.commitSubject; - s["commitBody"] = info.commitBody; - s["describe"] = info.describe; - s["branchName"] = info.branchName; - s["latestTag"] = info.latestTag; - s["latestTagName"] = info.latestTagName; - }; - - serialize(gtml::nabla_git_info, "nabla"); - serialize(gtml::dxc_git_info, "dxc"); - - const auto pretty = j.dump(4); - std::cout << pretty << std::endl; - - std::filesystem::path oPath = "build-info.json"; - - // TOOD: use argparse for it - if (argc > 3 && argv[2] == "--file") - oPath = argv[3]; - - std::ofstream outFile(oPath); - if (outFile.is_open()) - { - outFile << pretty; - outFile.close(); - printf("Saved \"%s\"\n", oPath.string().c_str()); - } - else - { - printf("Failed to open \"%s\" for writing\n", oPath.string().c_str()); - exit(-1); - } - - // in this mode terminate with 0 if all good - exit(0); - } - } - - if (not isAPILoaded()) - { - std::cerr << "Could not load Nabla API, terminating!"; - return false; - } - - if (system) - m_system = std::move(system); - else - m_system = system::IApplicationFramework::createSystem(); - - if (!m_system) - return false; - - m_logger = make_smart_refctd_ptr(core::bitflag(ILogger::ELL_DEBUG) | ILogger::ELL_INFO | ILogger::ELL_WARNING | ILogger::ELL_PERFORMANCE | ILogger::ELL_ERROR); - - if (insufficientArguments) - { - m_logger->log("Insufficient arguments.", ILogger::ELL_ERROR); - return false; - } - - m_arguments = std::vector(argv.begin() + 1, argv.end()-1); // turn argv into vector for convenience - - std::string file_to_compile = argv.back(); - - if (!m_system->exists(file_to_compile, IFileBase::ECF_READ)) { - m_logger->log("Incorrect arguments. Expecting last argument to be filename of the shader intended to compile.", ILogger::ELL_ERROR); - return false; - } - std::string output_filepath = ""; - - auto builtin_flag_pos = std::find(m_arguments.begin(), m_arguments.end(), "-no-nbl-builtins"); - if (builtin_flag_pos != m_arguments.end()) { - m_logger->log("Unmounting builtins."); - m_system->unmountBuiltins(); - no_nbl_builtins = true; - m_arguments.erase(builtin_flag_pos); - } - - auto split = [&](const std::string& str, char delim) - { - std::vector strings; - size_t start, end = 0; - - while ((start = str.find_first_not_of(delim, end)) != std::string::npos) - { - end = str.find(delim, start); - strings.push_back(str.substr(start, end - start)); - } - - return strings; - }; - - auto findOutputFlag = [&](const std::string_view& outputFlag) - { - return std::find_if(m_arguments.begin(), m_arguments.end(), [&](const std::string& argument) - { - return argument.find(outputFlag.data()) != std::string::npos; - }); - }; - - auto output_flag_pos_fc = findOutputFlag("-Fc"); - auto output_flag_pos_fo = findOutputFlag("-Fo"); - if (output_flag_pos_fc != m_arguments.end() && output_flag_pos_fo != m_arguments.end()) { - m_logger->log("Invalid arguments. Passed both -Fo and -Fc.", ILogger::ELL_ERROR); - return false; - } - auto output_flag_pos = output_flag_pos_fc != m_arguments.end() ? output_flag_pos_fc : output_flag_pos_fo; - if (output_flag_pos == m_arguments.end()) - { - m_logger->log("Missing arguments. Expecting `-Fc {filename}` or `-Fo {filename}`.", ILogger::ELL_ERROR); - return false; - } - else - { - // we need to assume -Fc may be passed with output file name quoted together with "", so we split it (DXC does it) - const auto& outputFlag = *output_flag_pos; - auto outputFlagVector = split(outputFlag, ' '); - - if(outputFlag == "-Fc" || outputFlag == "-Fo") - { - if (output_flag_pos + 1 != m_arguments.end()) - { - output_filepath = *(output_flag_pos + 1); - } - else - { - m_logger->log("Incorrect arguments. Expecting filename after %s.", ILogger::ELL_ERROR, outputFlag); - return false; - } - } - else - { - output_filepath = outputFlagVector[1]; - } - m_arguments.erase(output_flag_pos, output_flag_pos+2); - - if (output_filepath.empty()) - { - m_logger->log("Invalid output file path!" + output_filepath, ILogger::ELL_ERROR); - return false; - } - - m_logger->log("Compiled shader code will be saved to " + output_filepath, ILogger::ELL_INFO); - } + TrimStdoutLogger(const bitflag logLevelMask) : CStdoutLogger(logLevelMask) {} + +protected: + void threadsafeLog_impl(const std::string_view& fmt, E_LOG_LEVEL logLevel, va_list args) override + { + const auto str = constructLogString(fmt, logLevel, args); + size_t size = str.size(); + while (size && str[size - 1] == '\0') + --size; + if (!size) + return; + std::fwrite(str.data(), 1, size, stdout); + std::fflush(stdout); + } +}; -#ifndef NBL_EMBED_BUILTIN_RESOURCES - if (!no_nbl_builtins) { - m_system->unmountBuiltins(); - no_nbl_builtins = true; - m_logger->log("nsc.exe was compiled with builtin resources disabled. Force enabling -no-nbl-builtins.", ILogger::ELL_WARNING); - } -#endif - if (std::find(m_arguments.begin(), m_arguments.end(), "-E") == m_arguments.end()) - { - //Insert '-E main' into arguments if no entry point is specified - m_arguments.push_back("-E"); - m_arguments.push_back("main"); - } - - for (size_t i = 0; i < m_arguments.size() - 1; ++i) // -I must be given with second arg, no need to include iteration over last one - { - const auto& arg = m_arguments[i]; - if (arg == "-I") - m_include_search_paths.emplace_back(m_arguments[i + 1]); - } - - auto [shader, shaderStage] = open_shader_file(file_to_compile); - if (shader->getContentType() != IShader::E_CONTENT_TYPE::ECT_HLSL) - { - m_logger->log("Error. Loaded shader file content is not HLSL.", ILogger::ELL_ERROR); - return false; - } - - auto start = std::chrono::high_resolution_clock::now(); - auto compilation_result = compile_shader(shader.get(), shaderStage, file_to_compile); - auto end = std::chrono::high_resolution_clock::now(); - - // writie compiled shader to file as bytes - if (compilation_result) - { - m_logger->log("Shader compilation successful.", ILogger::ELL_INFO); - const auto took = std::to_string(std::chrono::duration_cast(end - start).count()); - m_logger->log("Took %s ms.", ILogger::ELL_PERFORMANCE, took.c_str()); - { - const auto location = std::filesystem::path(output_filepath); - const auto parentDirectory = location.parent_path(); - - if (!std::filesystem::exists(parentDirectory)) - { - if (!std::filesystem::create_directories(parentDirectory)) - { - m_logger->log("Failed to create parent directory for the " + output_filepath + "output!", ILogger::ELL_ERROR); - return false; - } - } - } - - std::fstream output_file(output_filepath, std::ios::out | std::ios::binary); - - if (!output_file.is_open()) - { - m_logger->log("Failed to open output file: " + output_filepath, ILogger::ELL_ERROR); - return false; - } - - output_file.write((const char*)compilation_result->getContent()->getPointer(), compilation_result->getContent()->getSize()); - - if (output_file.fail()) - { - m_logger->log("Failed to write to output file: " + output_filepath, ILogger::ELL_ERROR); - output_file.close(); - return false; - } - - output_file.close(); - - if (output_file.fail()) - { - m_logger->log("Failed to close output file: " + output_filepath, ILogger::ELL_ERROR); - return false; - } - - return true; - } - else - { - m_logger->log("Shader compilation failed.", ILogger::ELL_ERROR); - return false; - } - } - - void workLoopBody() override {} - - bool keepRunning() override { return false; } +class TrimFileLogger final : public CFileLogger +{ +public: + using CFileLogger::CFileLogger; + +protected: + void threadsafeLog_impl(const std::string_view& fmt, E_LOG_LEVEL logLevel, va_list args) override + { + const auto str = constructLogString(fmt, logLevel, args); + size_t size = str.size(); + while (size && str[size - 1] == '\0') + --size; + if (!size) + return; + IFile::success_t succ; + m_file->write(succ, str.data(), m_pos, size); + m_pos += succ.getBytesProcessed(); + } +}; +class ShaderLogger final : public IThreadsafeLogger +{ +public: + ShaderLogger(smart_refctd_ptr system, path logPath, const bitflag fileMask, const bitflag consoleMask, const bool noLog) + : IThreadsafeLogger(fileMask | consoleMask), m_system(std::move(system)), m_logPath(std::move(logPath)), m_fileMask(fileMask), m_consoleMask(consoleMask), m_noLog(noLog) + { + m_stdoutLogger = make_smart_refctd_ptr(m_consoleMask); + beginBuild(); + } + + void beginBuild() + { + m_fileLogger = nullptr; + m_file = nullptr; + + if (m_noLog) + return; + if (!m_system || m_logPath.empty()) + return; + + const auto parent = std::filesystem::path(m_logPath).parent_path(); + if (!parent.empty() && !std::filesystem::exists(parent)) + std::filesystem::create_directories(parent); + + for (auto attempt = 0u; attempt < kDeleteRetries; ++attempt) + { + if (m_system->deleteFile(m_logPath)) + break; + std::this_thread::sleep_for(kDeleteDelay); + } + + ISystem::future_t> fut; + m_system->createFile(fut, m_logPath, kLogFlags); + + if (fut.wait()) + { + auto lk = fut.acquire(); + if (lk) + lk.move_into(m_file); + } + + if (!m_file) + return; + + m_fileLogger = make_smart_refctd_ptr(smart_refctd_ptr(m_file), true, m_fileMask); + } private: + static constexpr auto kDeleteRetries = 3u; + static constexpr auto kDeleteDelay = std::chrono::milliseconds(100); + static constexpr auto kLogFlags = bitflag(IFileBase::ECF_WRITE) | IFileBase::ECF_SHARE_READ_WRITE | IFileBase::ECF_SHARE_DELETE; + + static inline std::string formatMessageOnly(const std::string_view& fmt, va_list args) + { + va_list a; + va_copy(a, args); + const int n = std::vsnprintf(nullptr, 0, fmt.data(), a); + va_end(a); + if (n <= 0) + return {}; + std::string s(size_t(n) + 1u, '\0'); + std::vsnprintf(s.data(), s.size(), fmt.data(), args); + s.resize(size_t(n)); + return s; + } + + void threadsafeLog_impl(const std::string_view& fmt, E_LOG_LEVEL logLevel, va_list args) override + { + const auto msg = formatMessageOnly(fmt, args); + if (msg.empty()) + return; + + if (m_stdoutLogger && (logLevel & m_consoleMask.value)) + m_stdoutLogger->log("%s", logLevel, msg.c_str()); + + if (m_noLog || !(logLevel & m_fileMask.value) || !m_fileLogger) + return; + + m_fileLogger->log("%s", logLevel, msg.c_str()); + } + + smart_refctd_ptr m_system; + smart_refctd_ptr m_file; + smart_refctd_ptr m_stdoutLogger; + smart_refctd_ptr m_fileLogger; + path m_logPath; + bitflag m_fileMask; + bitflag m_consoleMask; + bool m_noLog = false; +}; - core::smart_refctd_ptr compile_shader(const IShader* shader, hlsl::ShaderStage shaderStage, std::string_view sourceIdentifier) { - smart_refctd_ptr hlslcompiler = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); - - CHLSLCompiler::SOptions options = {}; - options.stage = shaderStage; - options.preprocessorOptions.sourceIdentifier = sourceIdentifier; - options.preprocessorOptions.logger = m_logger.get(); - - options.debugInfoFlags = core::bitflag(asset::IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_TOOL_BIT); - options.dxcOptions = std::span(m_arguments); - - auto includeFinder = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); - auto includeLoader = includeFinder->getDefaultFileSystemLoader(); - - // because before real compilation we do preprocess the input it doesn't really matter we proxy include search direcotries further with dxcOptions since at the end all includes are resolved to single file - for(const auto& it : m_include_search_paths) - includeFinder->addSearchPath(it, includeLoader); - - options.preprocessorOptions.includeFinder = includeFinder.get(); - - return hlslcompiler->compileToSPIRV((const char*)shader->getContent()->getPointer(), options); - } - - - std::tuple, hlsl::ShaderStage> open_shader_file(std::string filepath) { - - m_assetMgr = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); - - IAssetLoader::SAssetLoadParams lp = {}; - lp.logger = m_logger.get(); - lp.workingDirectory = localInputCWD; - auto assetBundle = m_assetMgr->getAsset(filepath, lp); - const auto assets = assetBundle.getContents(); - const auto* metadata = assetBundle.getMetadata(); - if (assets.empty()) { - m_logger->log("Could not load shader %s", ILogger::ELL_ERROR, filepath); - return {nullptr, hlsl::ShaderStage::ESS_UNKNOWN}; - } - assert(assets.size() == 1); - - // could happen when the file is missing an extension and we can't deduce its a shader - if (assetBundle.getAssetType() == IAsset::ET_BUFFER) - { - auto buf = IAsset::castDown(assets[0]); - std::string source; source.resize(buf->getSize()+1); - memcpy(source.data(),buf->getPointer(),buf->getSize()); - return { core::make_smart_refctd_ptr(source.data(), IShader::E_CONTENT_TYPE::ECT_HLSL, std::move(filepath)), hlsl::ShaderStage::ESS_UNKNOWN}; - } - else if (assetBundle.getAssetType() == IAsset::ET_SHADER) - { - const auto hlslMetadata = static_cast(metadata); - return { smart_refctd_ptr_static_cast(assets[0]), hlslMetadata->shaderStages->front()}; - } - else - { - m_logger->log("file '%s' is an asset that is neither a buffer or a shader.", ILogger::ELL_ERROR, filepath); - } - - return {nullptr, hlsl::ShaderStage::ESS_UNKNOWN}; - } +class ShaderCompiler final : public IApplicationFramework +{ + using base_t = IApplicationFramework; +public: + using base_t::base_t; + + bool onAppInitialized(smart_refctd_ptr&& system) override + { + const auto rawArgs = std::vector(argv.begin(), argv.end()); + const auto expandedArgs = expandJoinedArgs(rawArgs); + + argparse::ArgumentParser program("nsc"); + program.add_argument("--dump-build-info").default_value(false).implicit_value(true); + program.add_argument("--file").default_value(std::string{}); + program.add_argument("-P").default_value(false).implicit_value(true); + program.add_argument("-no-nbl-builtins").default_value(false).implicit_value(true); + program.add_argument("-MD").default_value(false).implicit_value(true); + program.add_argument("-M").default_value(false).implicit_value(true); + program.add_argument("-MF").default_value(std::string{}); + program.add_argument("-Fo").default_value(std::string{}); + program.add_argument("-Fc").default_value(std::string{}); + program.add_argument("-log").default_value(std::string{}); + program.add_argument("-nolog").default_value(false).implicit_value(true); + program.add_argument("-quiet").default_value(false).implicit_value(true); + program.add_argument("-verbose").default_value(false).implicit_value(true); + + std::vector unknownArgs; + try + { + unknownArgs = program.parse_known_args(expandedArgs); + } + catch (const std::runtime_error& err) + { + std::cerr << err.what() << std::endl << program; + return false; + } + + if (program.get("--dump-build-info")) + { + dumpBuildInfo(program); + std::exit(0); + } + + if (!isAPILoaded()) + { + std::cerr << "Could not load Nabla API, terminating!"; + return false; + } + + m_system = system ? std::move(system) : IApplicationFramework::createSystem(); + if (!m_system) + return false; + + if (rawArgs.size() < 2) + { + std::cerr << "Insufficient arguments.\n"; + return false; + } + + const std::string fileToCompile = rawArgs.back(); + if (!m_system->exists(fileToCompile, IFileBase::ECF_READ)) + { + std::cerr << "Input shader file does not exist: " << fileToCompile << "\n"; + return false; + } + + const bool preprocessOnly = program.get("-P"); + const bool hasFc = program.is_used("-Fc"); + const bool hasFo = program.is_used("-Fo"); + + if (hasFc == hasFo) + { + if (hasFc) + std::cerr << "Invalid arguments. Passed both -Fo and -Fc.\n"; + else + std::cerr << "Missing arguments. Expecting `-Fc {filename}` or `-Fo {filename}`.\n"; + return false; + } + + const std::string outputFilepath = hasFc ? program.get("-Fc") : program.get("-Fo"); + if (outputFilepath.empty()) + { + std::cerr << "Invalid output file path.\n"; + return false; + } + + const bool quiet = program.get("-quiet"); + const bool verbose = program.get("-verbose"); + if (quiet && verbose) + { + std::cerr << "Invalid arguments. Passed both -quiet and -verbose.\n"; + return false; + } + + const bool noLog = program.get("-nolog"); + const std::string logPathOverride = program.is_used("-log") ? program.get("-log") : std::string{}; + if (noLog && !logPathOverride.empty()) + { + std::cerr << "Invalid arguments. Passed both -nolog and -log.\n"; + return false; + } + + const auto logPath = logPathOverride.empty() ? std::filesystem::path(outputFilepath).concat(".log") : std::filesystem::path(logPathOverride); + const auto fileMask = bitflag(ILogger::ELL_ALL); + const auto consoleMask = bitflag(ILogger::ELL_WARNING) | ILogger::ELL_ERROR; + + m_logger = make_smart_refctd_ptr(m_system, logPath, fileMask, consoleMask, noLog); + + m_arguments = std::move(unknownArgs); + if (!m_arguments.empty() && m_arguments.back() == fileToCompile) + m_arguments.pop_back(); + + bool noNblBuiltins = program.get("-no-nbl-builtins"); + if (noNblBuiltins) + { + m_logger->log("Unmounting builtins."); + m_system->unmountBuiltins(); + } + + DepfileConfig dep; + if (program.get("-MD") || program.get("-M") || program.is_used("-MF")) + dep.enabled = true; + if (program.is_used("-MF")) + dep.path = program.get("-MF"); + if (dep.enabled && dep.path.empty()) + dep.path = outputFilepath + ".d"; + if (dep.enabled) + m_logger->log("Dependency file will be saved to %s", ILogger::ELL_INFO, dep.path.c_str()); - bool no_nbl_builtins{ false }; - smart_refctd_ptr m_system; - smart_refctd_ptr m_logger; - std::vector m_arguments, m_include_search_paths; - core::smart_refctd_ptr m_assetMgr; +#ifndef NBL_EMBED_BUILTIN_RESOURCES + if (!noNblBuiltins) + { + m_system->unmountBuiltins(); + noNblBuiltins = true; + m_logger->log("nsc.exe was compiled with builtin resources disabled. Force enabling -no-nbl-builtins.", ILogger::ELL_WARNING); + } +#endif + if (std::find(m_arguments.begin(), m_arguments.end(), "-E") == m_arguments.end()) + { + m_arguments.push_back("-E"); + m_arguments.push_back("main"); + } + + for (size_t i = 0; i + 1 < m_arguments.size(); ++i) + { + if (m_arguments[i] == "-I") + m_include_search_paths.emplace_back(m_arguments[i + 1]); + } + + const char* const action = preprocessOnly ? "Preprocessing" : "Compiling"; + const char* const outType = preprocessOnly ? "Preprocessed" : "Compiled"; + m_logger->log("%s %s", ILogger::ELL_INFO, action, fileToCompile.c_str()); + m_logger->log("%s shader code will be saved to %s", ILogger::ELL_INFO, outType, outputFilepath.c_str()); + + auto [shader, shaderStage] = open_shader_file(fileToCompile); + if (!shader || shader->getContentType() != IShader::E_CONTENT_TYPE::ECT_HLSL) + { + m_logger->log("Error. Loaded shader file content is not HLSL.", ILogger::ELL_ERROR); + return false; + } + + const auto start = std::chrono::high_resolution_clock::now(); + const auto job = runShaderJob(shader.get(), shaderStage, fileToCompile, dep, preprocessOnly); + const auto end = std::chrono::high_resolution_clock::now(); + + const char* const op = preprocessOnly ? "preprocessing" : "compilation"; + if (!job.ok) + { + m_logger->log("Shader %s failed.", ILogger::ELL_ERROR, op); + return false; + } + + const auto took = std::to_string(std::chrono::duration_cast(end - start).count()); + m_logger->log("Shader %s successful.", ILogger::ELL_INFO, op); + m_logger->log("Took %s ms.", ILogger::ELL_PERFORMANCE, took.c_str()); + + const auto outParent = std::filesystem::path(outputFilepath).parent_path(); + if (!outParent.empty() && !std::filesystem::exists(outParent)) + { + if (!std::filesystem::create_directories(outParent)) + { + m_logger->log("Failed to create parent directory for output %s.", ILogger::ELL_ERROR, outputFilepath.c_str()); + return false; + } + } + + std::fstream out(outputFilepath, std::ios::out | std::ios::binary); + if (!out.is_open()) + { + m_logger->log("Failed to open output file: %s", ILogger::ELL_ERROR, outputFilepath.c_str()); + return false; + } + + out.write(job.view.data(), job.view.size()); + if (out.fail()) + { + m_logger->log("Failed to write to output file: %s", ILogger::ELL_ERROR, outputFilepath.c_str()); + out.close(); + return false; + } + + out.close(); + if (out.fail()) + { + m_logger->log("Failed to close output file: %s", ILogger::ELL_ERROR, outputFilepath.c_str()); + return false; + } + + if (dep.enabled) + m_logger->log("Dependency file written to %s", ILogger::ELL_INFO, dep.path.c_str()); + + return true; + } + + void workLoopBody() override {} + bool keepRunning() override { return false; } +private: + struct DepfileConfig + { + bool enabled = false; + std::string path; + }; + + struct RunResult + { + bool ok = false; + std::string text; + smart_refctd_ptr compiled; + std::string_view view; + }; + + static std::vector expandJoinedArgs(const std::vector& args) + { + std::vector out; + out.reserve(args.size()); + + auto split = [&](const std::string& a, const char* p) + { + const size_t n = std::strlen(p); + if (a.rfind(p, 0) == 0 && a.size() > n) + { + out.emplace_back(p); + out.emplace_back(a.substr(n)); + return true; + } + return false; + }; + + for (const auto& a : args) + { + if (split(a, "-MF")) continue; + if (split(a, "-Fo")) continue; + if (split(a, "-Fc")) continue; + out.push_back(a); + } + + return out; + } + + static void dumpBuildInfo(const argparse::ArgumentParser& program) + { + json j; + auto& modules = j["modules"]; + + auto serialize = [&](const gtml::GitInfo& info, std::string_view target) + { + auto& s = modules[target.data()]; + s["isPopulated"] = info.isPopulated; + s["hasUncommittedChanges"] = info.hasUncommittedChanges.has_value() ? json(info.hasUncommittedChanges.value()) : json("UNKNOWN, BUILT WITHOUT DIRTY-CHANGES CAPTURE"); + s["commitAuthorName"] = info.commitAuthorName; + s["commitAuthorEmail"] = info.commitAuthorEmail; + s["commitHash"] = info.commitHash; + s["commitShortHash"] = info.commitShortHash; + s["commitDate"] = info.commitDate; + s["commitSubject"] = info.commitSubject; + s["commitBody"] = info.commitBody; + s["describe"] = info.describe; + s["branchName"] = info.branchName; + s["latestTag"] = info.latestTag; + s["latestTagName"] = info.latestTagName; + }; + + serialize(gtml::nabla_git_info, "nabla"); + serialize(gtml::dxc_git_info, "dxc"); + + const auto pretty = j.dump(4); + std::cout << pretty << std::endl; + + std::filesystem::path oPath = "build-info.json"; + if (program.is_used("--file")) + { + const auto filePath = program.get("--file"); + if (!filePath.empty()) + oPath = filePath; + } + + std::ofstream outFile(oPath); + if (!outFile.is_open()) + { + std::printf("Failed to open \"%s\" for writing\n", oPath.string().c_str()); + std::exit(-1); + } + + outFile << pretty; + std::printf("Saved \"%s\"\n", oPath.string().c_str()); + } + + RunResult runShaderJob(const IShader* shader, hlsl::ShaderStage shaderStage, std::string_view sourceIdentifier, const DepfileConfig& dep, const bool preprocessOnly) + { + RunResult r; + auto hlslcompiler = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); + + auto includeFinder = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); + auto includeLoader = includeFinder->getDefaultFileSystemLoader(); + for (const auto& p : m_include_search_paths) + includeFinder->addSearchPath(p, includeLoader); + + // need this struct becuase fields of IShaderCompiler::SMacroDefinition are string views + struct SMacroDefinitionBuffer + { + std::string identifier; + std::string definition; + }; + + core::vector macroDefinitionBuffers; + core::vector macroDefinitions; + { + for (const auto& argument : m_arguments) + { + if (argument.rfind("-D", 0) != 0) + continue; + + std::string argumentTmp = argument.substr(2); + + std::string identifier; + std::string definition; + + const size_t equalPos = argumentTmp.find('='); + if (equalPos == std::string::npos) + { + identifier = argumentTmp; + definition = "1"; + } + else + { + identifier = argumentTmp.substr(0, equalPos); + definition = argumentTmp.substr(equalPos + 1); + } + + macroDefinitionBuffers.emplace_back(identifier, definition); + } + + macroDefinitions.reserve(macroDefinitionBuffers.size()); + + for (const auto& macroDefinitionBuffer : macroDefinitionBuffers) + macroDefinitions.emplace_back(macroDefinitionBuffer.identifier, macroDefinitionBuffer.definition); + } + + if (preprocessOnly) + { + CHLSLCompiler::SPreprocessorOptions opt = {}; + opt.sourceIdentifier = sourceIdentifier; + opt.logger = m_logger.get(); + opt.includeFinder = includeFinder.get(); + opt.depfile = dep.enabled; + opt.depfilePath = dep.path; + opt.extraDefines = macroDefinitions; + + const char* codePtr = (const char*)shader->getContent()->getPointer(); + std::string_view code(codePtr, std::strlen(codePtr)); + + r.text = hlslcompiler->preprocessShader(std::string(code), shaderStage, opt, nullptr); + r.ok = !r.text.empty(); + r.view = r.text; + return r; + } + + CHLSLCompiler::SOptions opt = {}; + opt.stage = shaderStage; + opt.preprocessorOptions.sourceIdentifier = sourceIdentifier; + opt.preprocessorOptions.logger = m_logger.get(); + opt.preprocessorOptions.includeFinder = includeFinder.get(); + opt.preprocessorOptions.depfile = dep.enabled; + opt.preprocessorOptions.depfilePath = dep.path; + opt.preprocessorOptions.extraDefines = macroDefinitions; + opt.debugInfoFlags = bitflag(IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_TOOL_BIT); + opt.dxcOptions = std::span(m_arguments); + + r.compiled = hlslcompiler->compileToSPIRV((const char*)shader->getContent()->getPointer(), opt); + r.ok = bool(r.compiled); + if (r.ok) + r.view = { (const char*)r.compiled->getContent()->getPointer(), r.compiled->getContent()->getSize() }; + + return r; + } + + std::tuple, hlsl::ShaderStage> open_shader_file(std::string filepath) + { + m_assetMgr = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); + + IAssetLoader::SAssetLoadParams lp = {}; + lp.logger = m_logger.get(); + lp.workingDirectory = localInputCWD; + + auto bundle = m_assetMgr->getAsset(filepath, lp); + const auto assets = bundle.getContents(); + const auto* metadata = bundle.getMetadata(); + + if (assets.empty()) + { + m_logger->log("Could not load shader %s", ILogger::ELL_ERROR, filepath.c_str()); + return { nullptr, hlsl::ShaderStage::ESS_UNKNOWN }; + } + + if (bundle.getAssetType() == IAsset::ET_BUFFER) + { + auto buf = IAsset::castDown(assets[0]); + std::string source; + source.resize(buf->getSize() + 1); + std::memcpy(source.data(), buf->getPointer(), buf->getSize()); + return { make_smart_refctd_ptr(source.data(), IShader::E_CONTENT_TYPE::ECT_HLSL, std::move(filepath)), hlsl::ShaderStage::ESS_UNKNOWN }; + } + + if (bundle.getAssetType() == IAsset::ET_SHADER) + { + const auto hlslMetadata = static_cast(metadata); + return { smart_refctd_ptr_static_cast(assets[0]), hlslMetadata->shaderStages->front() }; + } + + m_logger->log("file '%s' is an asset that is neither a buffer or a shader.", ILogger::ELL_ERROR, filepath.c_str()); + return { nullptr, hlsl::ShaderStage::ESS_UNKNOWN }; + } + + smart_refctd_ptr m_system; + smart_refctd_ptr m_logger; + std::vector m_arguments, m_include_search_paths; + smart_refctd_ptr m_assetMgr; }; NBL_MAIN_FUNC(ShaderCompiler)