diff --git a/03_DeviceSelectionAndSharedSources/main.cpp b/03_DeviceSelectionAndSharedSources/main.cpp index b8fd3d18b..bcc849a4d 100644 --- a/03_DeviceSelectionAndSharedSources/main.cpp +++ b/03_DeviceSelectionAndSharedSources/main.cpp @@ -257,7 +257,7 @@ class DeviceSelectionAndSharedSourcesApp final : public application_templates::M } const auto* metadata = assetBundle.getMetadata(); - const auto hlslMetadata = static_cast(metadata); + const auto hlslMetadata = static_cast(metadata); const auto shaderStage = hlslMetadata->shaderStages->front(); // It would be super weird if loading a shader from a file produced more than 1 asset diff --git a/05_StreamingAndBufferDeviceAddressApp/CMakeLists.txt b/05_StreamingAndBufferDeviceAddressApp/CMakeLists.txt index a434ff32a..55ebaf41d 100644 --- a/05_StreamingAndBufferDeviceAddressApp/CMakeLists.txt +++ b/05_StreamingAndBufferDeviceAddressApp/CMakeLists.txt @@ -21,4 +21,49 @@ if(NBL_EMBED_BUILTIN_RESOURCES) ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) -endif() \ No newline at end of file +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/common.hlsl + app_resources/shader.comp.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/shader.comp.hlsl", + "KEY": "shader", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) \ No newline at end of file diff --git a/05_StreamingAndBufferDeviceAddressApp/app_resources/shader.comp.hlsl b/05_StreamingAndBufferDeviceAddressApp/app_resources/shader.comp.hlsl index af38ffada..31c60aefd 100644 --- a/05_StreamingAndBufferDeviceAddressApp/app_resources/shader.comp.hlsl +++ b/05_StreamingAndBufferDeviceAddressApp/app_resources/shader.comp.hlsl @@ -1,12 +1,9 @@ #include "common.hlsl" -// just a small test -#include "nbl/builtin/hlsl/jit/device_capabilities.hlsl" - [[vk::push_constant]] PushConstantData pushConstants; // does absolutely nothing, a later example will show how it gets used -template +template void dummyTraitTest() {} [numthreads(WorkgroupSize,1,1)] diff --git a/05_StreamingAndBufferDeviceAddressApp/main.cpp b/05_StreamingAndBufferDeviceAddressApp/main.cpp index b82dc18ca..ab0984a07 100644 --- a/05_StreamingAndBufferDeviceAddressApp/main.cpp +++ b/05_StreamingAndBufferDeviceAddressApp/main.cpp @@ -6,6 +6,7 @@ // I've moved out a tiny part of this example into a shared header for reuse, please open and read it. #include "nbl/application_templates/MonoDeviceApplication.hpp" #include "nbl/examples/common/BuiltinResourcesApplication.hpp" +#include "nbl/this_example/builtin/build/spirv/keys.hpp" using namespace nbl; @@ -95,15 +96,15 @@ class StreamingAndBufferDeviceAddressApp final : public application_templates::M { IAssetLoader::SAssetLoadParams lp = {}; lp.logger = m_logger.get(); - lp.workingDirectory = ""; // virtual root - auto assetBundle = m_assetMgr->getAsset("app_resources/shader.comp.hlsl",lp); + lp.workingDirectory = "app_resources"; // virtual root + + auto key = nbl::this_example::builtin::build::get_spirv_key<"shader">(m_device.get()); + auto assetBundle = m_assetMgr->getAsset(key.data(), lp); const auto assets = assetBundle.getContents(); if (assets.empty()) return logFail("Could not load shader!"); - // lets go straight from ICPUSpecializedShader to IGPUSpecializedShader - const auto shaderSource = IAsset::castDown(assets[0]); - shader = m_device->compileShader({shaderSource.get()}); + shader = IAsset::castDown(assets[0]); // The down-cast should not fail! assert(shader); } diff --git a/07_StagingAndMultipleQueues/CMakeLists.txt b/07_StagingAndMultipleQueues/CMakeLists.txt index a434ff32a..fe063be7c 100644 --- a/07_StagingAndMultipleQueues/CMakeLists.txt +++ b/07_StagingAndMultipleQueues/CMakeLists.txt @@ -21,4 +21,49 @@ if(NBL_EMBED_BUILTIN_RESOURCES) ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) -endif() \ No newline at end of file +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/common.hlsl + app_resources/comp_shader.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/comp_shader.hlsl", + "KEY": "comp_shader", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) \ No newline at end of file diff --git a/07_StagingAndMultipleQueues/app_resources/common.hlsl b/07_StagingAndMultipleQueues/app_resources/common.hlsl index 259d5069d..de15810c9 100644 --- a/07_StagingAndMultipleQueues/app_resources/common.hlsl +++ b/07_StagingAndMultipleQueues/app_resources/common.hlsl @@ -1,8 +1,8 @@ #include "nbl/builtin/hlsl/cpp_compat.hlsl" -NBL_CONSTEXPR uint32_t WorkgroupSizeX = 16; -NBL_CONSTEXPR uint32_t WorkgroupSizeY = 16; -NBL_CONSTEXPR uint32_t WorkgroupSize = WorkgroupSizeX*WorkgroupSizeY; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t WorkgroupSizeX = 16; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t WorkgroupSizeY = 16; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t WorkgroupSize = WorkgroupSizeX*WorkgroupSizeY; static const uint32_t FRAMES_IN_FLIGHT = 3u; diff --git a/07_StagingAndMultipleQueues/main.cpp b/07_StagingAndMultipleQueues/main.cpp index fc6bf4551..a850c1c47 100644 --- a/07_StagingAndMultipleQueues/main.cpp +++ b/07_StagingAndMultipleQueues/main.cpp @@ -4,6 +4,7 @@ // I've moved out a tiny part of this example into a shared header for reuse, please open and read it. #include "nbl/examples/examples.hpp" +#include "nbl/this_example/builtin/build/spirv/keys.hpp" using namespace nbl; using namespace nbl::core; @@ -189,7 +190,7 @@ class StagingAndMultipleQueuesApp final : public application_templates::BasicMul for (uint32_t imageIdx = 0; imageIdx < IMAGE_CNT; ++imageIdx) { const auto imagePathToLoad = imagesToLoad[imageIdx]; - auto cpuImage = loadFistAssetInBundle(imagePathToLoad); + auto cpuImage = loadImageAsset(imagePathToLoad); if (!cpuImage) logFailAndTerminate("Failed to load image from path %s",ILogger::ELL_ERROR,imagePathToLoad); @@ -279,17 +280,10 @@ class StagingAndMultipleQueuesApp final : public application_templates::BasicMul } // LOAD SHADER FROM FILE - smart_refctd_ptr source; - { - source = loadFistAssetInBundle("../app_resources/comp_shader.hlsl"); - } + smart_refctd_ptr shader = loadPreCompiledShader<"comp_shader">(); // "../app_resources/comp_shader.hlsl" - if (!source) - logFailAndTerminate("Could not create a CPU shader!"); - - core::smart_refctd_ptr shader = m_device->compileShader({ source.get() }); - if(!shader) - logFailAndTerminate("Could not compile shader to spirv!"); + if (!shader) + logFailAndTerminate("Could not load the precompiled shader!"); // CREATE COMPUTE PIPELINE SPushConstantRange pc[1]; @@ -534,21 +528,39 @@ class StagingAndMultipleQueuesApp final : public application_templates::BasicMul return false; } - - template - core::smart_refctd_ptr loadFistAssetInBundle(const std::string& path) + + core::smart_refctd_ptr loadImageAsset(const std::string& path) { IAssetLoader::SAssetLoadParams lp; SAssetBundle bundle = m_assetMgr->getAsset(path, lp); if (bundle.getContents().empty()) - logFailAndTerminate("Couldn't load an asset.",ILogger::ELL_ERROR); + logFailAndTerminate("Couldn't load an image.",ILogger::ELL_ERROR); - auto asset = IAsset::castDown(bundle.getContents()[0]); + auto asset = IAsset::castDown(bundle.getContents()[0]); if (!asset) logFailAndTerminate("Incorrect asset loaded.",ILogger::ELL_ERROR); return asset; } + + template + core::smart_refctd_ptr loadPreCompiledShader() + { + IAssetLoader::SAssetLoadParams lp; + lp.logger = m_logger.get(); + lp.workingDirectory = "app_resources"; + + auto key = nbl::this_example::builtin::build::get_spirv_key(m_device.get()); + SAssetBundle bundle = m_assetMgr->getAsset(key.data(), lp); + if (bundle.getContents().empty()) + logFailAndTerminate("Couldn't load a shader.", ILogger::ELL_ERROR); + + auto asset = IAsset::castDown(bundle.getContents()[0]); + if (!asset) + logFailAndTerminate("Incorrect asset loaded.", ILogger::ELL_ERROR); + + return asset; + } }; NBL_MAIN_FUNC(StagingAndMultipleQueuesApp) diff --git a/10_CountingSort/CMakeLists.txt b/10_CountingSort/CMakeLists.txt index b7cad41da..14bde428d 100644 --- a/10_CountingSort/CMakeLists.txt +++ b/10_CountingSort/CMakeLists.txt @@ -22,3 +22,70 @@ if(NBL_EMBED_BUILTIN_RESOURCES) LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/common.hlsl + app_resources/prefix_sum_shader.comp.hlsl + app_resources/scatter_shader.comp.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(REQUIRED_CAPS [=[ + { + "kind": "limits", + "name": "maxComputeWorkGroupInvocations", + "type": "uint32_t", + "values": [256,512,1024] + }, + { + "kind": "limits", + "name": "maxComputeSharedMemorySize", + "type": "uint32_t", + "values": [16384, 32768, 65536] + } +]=]) + +set(JSON [=[ +[ + { + "INPUT": "app_resources/prefix_sum_shader.comp.hlsl", + "KEY": "prefix_sum_shader", + "CAPS": [${REQUIRED_CAPS}] + }, + { + "INPUT": "app_resources/scatter_shader.comp.hlsl", + "KEY": "scatter_shader", + "CAPS": [${REQUIRED_CAPS}] + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) diff --git a/10_CountingSort/app_resources/common.hlsl b/10_CountingSort/app_resources/common.hlsl index bcbf01727..1074432b0 100644 --- a/10_CountingSort/app_resources/common.hlsl +++ b/10_CountingSort/app_resources/common.hlsl @@ -22,6 +22,10 @@ using namespace nbl::hlsl; #ifdef __HLSL_VERSION #include "nbl/builtin/hlsl/bda/bda_accessor.hlsl" +static const uint32_t WorkgroupSize = DeviceConfigCaps::maxComputeWorkGroupInvocations; +static const uint32_t MaxBucketCount = (DeviceConfigCaps::maxComputeSharedMemorySize / sizeof(uint32_t)) / 2; +static const uint32_t BucketCount = (MaxBucketCount > 3000) ? 3000 : MaxBucketCount; + using Ptr = bda::__ptr; using PtrAccessor = BdaAccessor; @@ -54,6 +58,8 @@ uint32_t3 glsl::gl_WorkGroupSize() { return uint32_t3(WorkgroupSize, 1, 1); } + + #endif #endif \ No newline at end of file diff --git a/10_CountingSort/main.cpp b/10_CountingSort/main.cpp index d51650919..a22647750 100644 --- a/10_CountingSort/main.cpp +++ b/10_CountingSort/main.cpp @@ -1,4 +1,5 @@ #include "nbl/examples/examples.hpp" +#include "nbl/this_example/builtin/build/spirv/keys.hpp" using namespace nbl; using namespace nbl::core; @@ -32,19 +33,34 @@ class CountingSortApp final : public application_templates::MonoDeviceApplicatio return false; 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); + const uint32_t WorkgroupSize = limits.maxComputeWorkGroupInvocations; const uint32_t MaxBucketCount = (limits.maxComputeSharedMemorySize / sizeof(uint32_t)) / 2; constexpr uint32_t element_count = 100000; const uint32_t bucket_count = std::min((uint32_t)3000, MaxBucketCount); const uint32_t elements_per_thread = ceil((float)ceil((float)element_count / limits.computeUnits) / WorkgroupSize); - auto prepShader = [&](const core::string& path) -> smart_refctd_ptr + auto loadPrecompiledShader = [&]() -> smart_refctd_ptr { // this time we load a shader directly from a file IAssetLoader::SAssetLoadParams lp = {}; lp.logger = m_logger.get(); - lp.workingDirectory = ""; // virtual root - auto assetBundle = m_assetMgr->getAsset(path,lp); + lp.workingDirectory = "app_resources"; // virtual root + auto key = nbl::this_example::builtin::build::get_spirv_key(limits, m_physicalDevice->getFeatures()); + auto assetBundle = m_assetMgr->getAsset(key.data(), lp); const auto assets = assetBundle.getContents(); if (assets.empty()) { @@ -52,29 +68,24 @@ class CountingSortApp final : public application_templates::MonoDeviceApplicatio return nullptr; } - auto source = IAsset::castDown(assets[0]); + auto shader = IAsset::castDown(assets[0]); // The down-cast should not fail! - assert(source); + assert(shader); // There's two ways of doing stuff like this: // 1. this - modifying the asset after load // 2. creating a short shader source file that includes the asset you would have wanted to load - auto overrideSource = CHLSLCompiler::createOverridenCopy( - source.get(), "#define WorkgroupSize %d\n#define BucketCount %d\n", - WorkgroupSize, bucket_count - ); + // + //auto overrideSource = CHLSLCompiler::createOverridenCopy( + // source.get(), "#define WorkgroupSize %d\n#define BucketCount %d\n", + // WorkgroupSize, bucket_count + //); // this time we skip the use of the asset converter since the IShader->IGPUShader path is quick and simple - auto shader = m_device->compileShader({ overrideSource.get() }); - if (!shader) - { - logFail("Creation of Prefix Sum Shader from CPU Shader source failed!"); - return nullptr; - } return shader; }; - auto prefixSumShader = prepShader("app_resources/prefix_sum_shader.comp.hlsl"); - auto scatterShader = prepShader("app_resources/scatter_shader.comp.hlsl"); + auto prefixSumShader = loadPrecompiledShader.operator()<"prefix_sum_shader">(); // "app_resources/prefix_sum_shader.comp.hlsl" + auto scatterShader = loadPrecompiledShader.operator()<"scatter_shader">(); // "app_resources/scatter_shader.comp.hlsl" // People love Reflection but I prefer Shader Sources instead! const nbl::asset::SPushConstantRange pcRange = { .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE,.offset = 0,.size = sizeof(CountingPushData) }; diff --git a/11_FFT/CMakeLists.txt b/11_FFT/CMakeLists.txt index a434ff32a..ca9fe8428 100644 --- a/11_FFT/CMakeLists.txt +++ b/11_FFT/CMakeLists.txt @@ -21,4 +21,49 @@ if(NBL_EMBED_BUILTIN_RESOURCES) ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) -endif() \ No newline at end of file +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/common.hlsl + app_resources/shader.comp.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/shader.comp.hlsl", + "KEY": "shader", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) \ No newline at end of file diff --git a/11_FFT/main.cpp b/11_FFT/main.cpp index 1886da72a..49d157a38 100644 --- a/11_FFT/main.cpp +++ b/11_FFT/main.cpp @@ -2,6 +2,7 @@ // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#include "nbl/this_example/builtin/build/spirv/keys.hpp" #include "nbl/examples/examples.hpp" @@ -45,15 +46,6 @@ class FFT_Test final : public application_templates::MonoDeviceApplication, publ smart_refctd_ptr m_timeline; uint64_t semaphorValue = 0; - inline core::smart_refctd_ptr createShader( - const char* includeMainName) - { - std::string prelude = "#include \""; - auto hlslShader = core::make_smart_refctd_ptr((prelude + includeMainName + "\"\n").c_str(), IShader::E_CONTENT_TYPE::ECT_HLSL, includeMainName); - assert(hlslShader); - return m_device->compileShader({ hlslShader.get() }); - } - public: // Yay thanks to multiple inheritance we cannot forward ctors anymore FFT_Test(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) : @@ -68,28 +60,23 @@ class FFT_Test final : public application_templates::MonoDeviceApplication, publ if (!asset_base_t::onAppInitialized(std::move(system))) return false; - // this time we load a shader directly from a file smart_refctd_ptr shader; - /* { + { IAssetLoader::SAssetLoadParams lp = {}; lp.logger = m_logger.get(); - lp.workingDirectory = ""; // virtual root - auto assetBundle = m_assetMgr->getAsset("app_resources/shader.comp.hlsl", lp); + lp.workingDirectory = "app_resources"; // virtual root + auto key = nbl::this_example::builtin::build::get_spirv_key<"shader">(m_device.get()); + auto assetBundle = m_assetMgr->getAsset(key.data(), lp); const auto assets = assetBundle.getContents(); if (assets.empty()) return logFail("Could not load shader!"); // Cast down the asset to its proper type - auto source = IAsset::castDown(assets[0]); - // The down-cast should not fail! - assert(source); - - // Compile directly to SPIR-V Shader - shader = m_device->compileShader({ source.get() }); + shader = IAsset::castDown(assets[0]); + if (!shader) - return logFail("Creation of a SPIR-V Shader from HLSL Shader source failed!"); - }*/ - shader = createShader("app_resources/shader.comp.hlsl"); + return logFail("Invalid shader!"); + } // Create massive upload/download buffers constexpr uint32_t DownstreamBufferSize = sizeof(scalar_t) << 23; diff --git a/14_Mortons/CMakeLists.txt b/14_Mortons/CMakeLists.txt new file mode 100644 index 000000000..1c595e8bb --- /dev/null +++ b/14_Mortons/CMakeLists.txt @@ -0,0 +1,82 @@ +include(common RESULT_VARIABLE RES) +if(NOT RES) + message(FATAL_ERROR "common.cmake not found. Should be in {repo_root}/cmake directory") +endif() + +nbl_create_executable_project("" "" "" "" "${NBL_EXECUTABLE_PROJECT_CREATION_PCH_TARGET}") + +if(NBL_EMBED_BUILTIN_RESOURCES) + set(_BR_TARGET_ ${EXECUTABLE_NAME}_builtinResourceData) + set(RESOURCE_DIR "app_resources") + + get_filename_component(_SEARCH_DIRECTORIES_ "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) + get_filename_component(_OUTPUT_DIRECTORY_SOURCE_ "${CMAKE_CURRENT_BINARY_DIR}/src" ABSOLUTE) + get_filename_component(_OUTPUT_DIRECTORY_HEADER_ "${CMAKE_CURRENT_BINARY_DIR}/include" ABSOLUTE) + + file(GLOB_RECURSE BUILTIN_RESOURCE_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_DIR}" CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_DIR}/*") + foreach(RES_FILE ${BUILTIN_RESOURCE_FILES}) + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "${RES_FILE}") + endforeach() + + ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") + + LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) +endif() + +if(MSVC) + target_compile_options("${EXECUTABLE_NAME}" PUBLIC "/fp:strict") +else() + target_compile_options("${EXECUTABLE_NAME}" PUBLIC -ffloat-store -frounding-math -fsignaling-nans -ftrapping-math) +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/common.hlsl + app_resources/testCommon.hlsl + app_resources/testCommon2.hlsl + app_resources/test.comp.hlsl + app_resources/test2.comp.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/test.comp.hlsl", + "KEY": "test", + }, + { + "INPUT": "app_resources/test2.comp.hlsl", + "KEY": "test2", + }, +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) \ No newline at end of file diff --git a/14_Mortons/CTester.h b/14_Mortons/CTester.h new file mode 100644 index 000000000..e25fa58a2 --- /dev/null +++ b/14_Mortons/CTester.h @@ -0,0 +1,489 @@ +#ifndef _NBL_EXAMPLES_TESTS_12_MORTON_C_TESTER_INCLUDED_ +#define _NBL_EXAMPLES_TESTS_12_MORTON_C_TESTER_INCLUDED_ + +#include +#include "app_resources/testCommon.hlsl" +#include "app_resources/testCommon2.hlsl" +#include "nbl/examples/Tester/ITester.h" + +using namespace nbl; + +class CTester final : public ITester +{ + using base_t = ITester; + +public: + CTester(const uint32_t testBatchCount) + : base_t(testBatchCount) {}; + +private: + InputTestValues generateInputTestValues() override + { + std::uniform_int_distribution intDistribution(uint32_t(0), std::numeric_limits::max()); + std::uniform_int_distribution longDistribution(uint64_t(0), std::numeric_limits::max()); + + // Set input thest values that will be used in both CPU and GPU tests + InputTestValues testInput; + + testInput.generatedA = longDistribution(getRandomEngine()); + testInput.generatedB = longDistribution(getRandomEngine()); + + uint32_t generatedShift = intDistribution(getRandomEngine()) & uint32_t(63); + testInput.shift = generatedShift; + + testInput.coordX = longDistribution(getRandomEngine()); + testInput.coordY = longDistribution(getRandomEngine()); + testInput.coordZ = longDistribution(getRandomEngine()); + testInput.coordW = longDistribution(getRandomEngine()); + + return testInput; + } + + TestValues determineExpectedResults(const InputTestValues& testInput) override + { + // use std library or glm functions to determine expected test values, the output of functions from intrinsics.hlsl will be verified against these values + TestValues expected; + + { + const uint64_t generatedA = testInput.generatedA; + const uint64_t generatedB = testInput.generatedB; + const uint32_t generatedShift = testInput.shift; + + expected.emulatedAnd = _static_cast(generatedA & generatedB); + expected.emulatedOr = _static_cast(generatedA | generatedB); + expected.emulatedXor = _static_cast(generatedA ^ generatedB); + expected.emulatedNot = _static_cast(~generatedA); + expected.emulatedPlus = _static_cast(generatedA + generatedB); + expected.emulatedMinus = _static_cast(generatedA - generatedB); + expected.emulatedUnaryMinus = _static_cast(-generatedA); + expected.emulatedLess = uint32_t(generatedA < generatedB); + expected.emulatedLessEqual = uint32_t(generatedA <= generatedB); + expected.emulatedGreater = uint32_t(generatedA > generatedB); + expected.emulatedGreaterEqual = uint32_t(generatedA >= generatedB); + + expected.emulatedLeftShifted = _static_cast(generatedA << generatedShift); + expected.emulatedUnsignedRightShifted = _static_cast(generatedA >> generatedShift); + expected.emulatedSignedRightShifted = _static_cast(static_cast(generatedA) >> generatedShift); + } + { + uint64_t2 Vec2A = { testInput.coordX, testInput.coordY }; + uint64_t2 Vec2B = { testInput.coordZ, testInput.coordW }; + + uint16_t2 Vec2ASmall = createAnyBitIntegerVecFromU64Vec(Vec2A); + uint16_t2 Vec2BSmall = createAnyBitIntegerVecFromU64Vec(Vec2B); + uint16_t2 Vec2AMedium = createAnyBitIntegerVecFromU64Vec(Vec2A); + uint16_t2 Vec2BMedium = createAnyBitIntegerVecFromU64Vec(Vec2B); + uint32_t2 Vec2AFull = createAnyBitIntegerVecFromU64Vec(Vec2A); + uint32_t2 Vec2BFull = createAnyBitIntegerVecFromU64Vec(Vec2B); + + uint64_t3 Vec3A = { testInput.coordX, testInput.coordY, testInput.coordZ }; + uint64_t3 Vec3B = { testInput.coordY, testInput.coordZ, testInput.coordW }; + + uint16_t3 Vec3ASmall = createAnyBitIntegerVecFromU64Vec(Vec3A); + uint16_t3 Vec3BSmall = createAnyBitIntegerVecFromU64Vec(Vec3B); + uint16_t3 Vec3AMedium = createAnyBitIntegerVecFromU64Vec(Vec3A); + uint16_t3 Vec3BMedium = createAnyBitIntegerVecFromU64Vec(Vec3B); + uint32_t3 Vec3AFull = createAnyBitIntegerVecFromU64Vec(Vec3A); + uint32_t3 Vec3BFull = createAnyBitIntegerVecFromU64Vec(Vec3B); + + uint64_t4 Vec4A = { testInput.coordX, testInput.coordY, testInput.coordZ, testInput.coordW }; + uint64_t4 Vec4B = { testInput.coordY, testInput.coordZ, testInput.coordW, testInput.coordX }; + + uint16_t4 Vec4ASmall = createAnyBitIntegerVecFromU64Vec(Vec4A); + uint16_t4 Vec4BSmall = createAnyBitIntegerVecFromU64Vec(Vec4B); + uint16_t4 Vec4AMedium = createAnyBitIntegerVecFromU64Vec(Vec4A); + uint16_t4 Vec4BMedium = createAnyBitIntegerVecFromU64Vec(Vec4B); + uint16_t4 Vec4AFull = createAnyBitIntegerVecFromU64Vec(Vec4A); + uint16_t4 Vec4BFull = createAnyBitIntegerVecFromU64Vec(Vec4B); + + // Signed vectors can't just have their highest bits masked off, for them to preserve sign we also need to left shift then right shift them + // so their highest bits are all 0s or 1s depending on the sign of the number they encode + + int16_t2 Vec2ASignedSmall = createAnyBitIntegerVecFromU64Vec(Vec2A); + int16_t2 Vec2BSignedSmall = createAnyBitIntegerVecFromU64Vec(Vec2B); + int16_t2 Vec2ASignedMedium = createAnyBitIntegerVecFromU64Vec(Vec2A); + int16_t2 Vec2BSignedMedium = createAnyBitIntegerVecFromU64Vec(Vec2B); + int32_t2 Vec2ASignedFull = createAnyBitIntegerVecFromU64Vec(Vec2A); + int32_t2 Vec2BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec2B); + + int16_t3 Vec3ASignedSmall = createAnyBitIntegerVecFromU64Vec(Vec3A); + int16_t3 Vec3BSignedSmall = createAnyBitIntegerVecFromU64Vec(Vec3B); + int16_t3 Vec3ASignedMedium = createAnyBitIntegerVecFromU64Vec(Vec3A); + int16_t3 Vec3BSignedMedium = createAnyBitIntegerVecFromU64Vec(Vec3B); + int32_t3 Vec3ASignedFull = createAnyBitIntegerVecFromU64Vec(Vec3A); + int32_t3 Vec3BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec3B); + + int16_t4 Vec4ASignedSmall = createAnyBitIntegerVecFromU64Vec(Vec4A); + int16_t4 Vec4BSignedSmall = createAnyBitIntegerVecFromU64Vec(Vec4B); + int16_t4 Vec4ASignedMedium = createAnyBitIntegerVecFromU64Vec(Vec4A); + int16_t4 Vec4BSignedMedium = createAnyBitIntegerVecFromU64Vec(Vec4B); + int16_t4 Vec4ASignedFull = createAnyBitIntegerVecFromU64Vec(Vec4A); + int16_t4 Vec4BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec4B); + + // Plus + expected.mortonPlus_small_2 = createMortonFromU64Vec(Vec2ASmall + Vec2BSmall); + expected.mortonPlus_medium_2 = createMortonFromU64Vec(Vec2AMedium + Vec2BMedium); + expected.mortonPlus_full_2 = createMortonFromU64Vec(Vec2AFull + Vec2BFull); + expected.mortonPlus_emulated_2 = createMortonFromU64Vec(Vec2AFull + Vec2BFull); + + expected.mortonPlus_small_3 = createMortonFromU64Vec(Vec3ASmall + Vec3BSmall); + expected.mortonPlus_medium_3 = createMortonFromU64Vec(Vec3AMedium + Vec3BMedium); + expected.mortonPlus_full_3 = createMortonFromU64Vec(Vec3AFull + Vec3BFull); + expected.mortonPlus_emulated_3 = createMortonFromU64Vec(Vec3AFull + Vec3BFull); + + expected.mortonPlus_small_4 = createMortonFromU64Vec(Vec4ASmall + Vec4BSmall); + expected.mortonPlus_medium_4 = createMortonFromU64Vec(Vec4AMedium + Vec4BMedium); + expected.mortonPlus_full_4 = createMortonFromU64Vec(Vec4AFull + Vec4BFull); + expected.mortonPlus_emulated_4 = createMortonFromU64Vec(Vec4AFull + Vec4BFull); + + // Minus + expected.mortonMinus_small_2 = createMortonFromU64Vec(Vec2ASmall - Vec2BSmall); + expected.mortonMinus_medium_2 = createMortonFromU64Vec(Vec2AMedium - Vec2BMedium); + expected.mortonMinus_full_2 = createMortonFromU64Vec(Vec2AFull - Vec2BFull); + expected.mortonMinus_emulated_2 = createMortonFromU64Vec(Vec2AFull - Vec2BFull); + + expected.mortonMinus_small_3 = createMortonFromU64Vec(Vec3ASmall - Vec3BSmall); + expected.mortonMinus_medium_3 = createMortonFromU64Vec(Vec3AMedium - Vec3BMedium); + expected.mortonMinus_full_3 = createMortonFromU64Vec(Vec3AFull - Vec3BFull); + expected.mortonMinus_emulated_3 = createMortonFromU64Vec(Vec3AFull - Vec3BFull); + + expected.mortonMinus_small_4 = createMortonFromU64Vec(Vec4ASmall - Vec4BSmall); + expected.mortonMinus_medium_4 = createMortonFromU64Vec(Vec4AMedium - Vec4BMedium); + expected.mortonMinus_full_4 = createMortonFromU64Vec(Vec4AFull - Vec4BFull); + expected.mortonMinus_emulated_4 = createMortonFromU64Vec(Vec4AFull - Vec4BFull); + + // Coordinate-wise equality + expected.mortonEqual_small_2 = uint32_t2(glm::equal(Vec2ASmall, Vec2BSmall)); + expected.mortonEqual_medium_2 = uint32_t2(glm::equal(Vec2AMedium, Vec2BMedium)); + expected.mortonEqual_full_2 = uint32_t2(glm::equal(Vec2AFull, Vec2BFull)); + expected.mortonEqual_emulated_2 = uint32_t2(glm::equal(Vec2AFull, Vec2BFull)); + + expected.mortonEqual_small_3 = uint32_t3(glm::equal(Vec3ASmall, Vec3BSmall)); + expected.mortonEqual_medium_3 = uint32_t3(glm::equal(Vec3AMedium, Vec3BMedium)); + expected.mortonEqual_full_3 = uint32_t3(glm::equal(Vec3AFull, Vec3BFull)); + expected.mortonEqual_emulated_3 = uint32_t3(glm::equal(Vec3AFull, Vec3BFull)); + + expected.mortonEqual_small_4 = uint32_t4(glm::equal(Vec4ASmall, Vec4BSmall)); + expected.mortonEqual_medium_4 = uint32_t4(glm::equal(Vec4AMedium, Vec4BMedium)); + expected.mortonEqual_full_4 = uint32_t4(glm::equal(Vec4AFull, Vec4BFull)); + expected.mortonEqual_emulated_4 = uint32_t4(glm::equal(Vec4AFull, Vec4BFull)); + + // Coordinate-wise unsigned inequality (just testing with less) + expected.mortonUnsignedLess_small_2 = uint32_t2(glm::lessThan(Vec2ASmall, Vec2BSmall)); + expected.mortonUnsignedLess_medium_2 = uint32_t2(glm::lessThan(Vec2AMedium, Vec2BMedium)); + expected.mortonUnsignedLess_full_2 = uint32_t2(glm::lessThan(Vec2AFull, Vec2BFull)); + expected.mortonUnsignedLess_emulated_2 = uint32_t2(glm::lessThan(Vec2AFull, Vec2BFull)); + + expected.mortonUnsignedLess_small_3 = uint32_t3(glm::lessThan(Vec3ASmall, Vec3BSmall)); + expected.mortonUnsignedLess_medium_3 = uint32_t3(glm::lessThan(Vec3AMedium, Vec3BMedium)); + expected.mortonUnsignedLess_full_3 = uint32_t3(glm::lessThan(Vec3AFull, Vec3BFull)); + expected.mortonUnsignedLess_emulated_3 = uint32_t3(glm::lessThan(Vec3AFull, Vec3BFull)); + + expected.mortonUnsignedLess_small_4 = uint32_t4(glm::lessThan(Vec4ASmall, Vec4BSmall)); + expected.mortonUnsignedLess_medium_4 = uint32_t4(glm::lessThan(Vec4AMedium, Vec4BMedium)); + expected.mortonUnsignedLess_full_4 = uint32_t4(glm::lessThan(Vec4AFull, Vec4BFull)); + expected.mortonUnsignedLess_emulated_4 = uint32_t4(glm::lessThan(Vec4AFull, Vec4BFull)); + + // Coordinate-wise signed inequality + expected.mortonSignedLess_small_2 = uint32_t2(glm::lessThan(Vec2ASignedSmall, Vec2BSignedSmall)); + expected.mortonSignedLess_medium_2 = uint32_t2(glm::lessThan(Vec2ASignedMedium, Vec2BSignedMedium)); + expected.mortonSignedLess_full_2 = uint32_t2(glm::lessThan(Vec2ASignedFull, Vec2BSignedFull)); + expected.mortonSignedLess_emulated_2 = uint32_t2(glm::lessThan(Vec2ASignedFull, Vec2BSignedFull)); + + expected.mortonSignedLess_small_3 = uint32_t3(glm::lessThan(Vec3ASignedSmall, Vec3BSignedSmall)); + expected.mortonSignedLess_medium_3 = uint32_t3(glm::lessThan(Vec3ASignedMedium, Vec3BSignedMedium)); + expected.mortonSignedLess_full_3 = uint32_t3(glm::lessThan(Vec3ASignedFull, Vec3BSignedFull)); + expected.mortonSignedLess_emulated_3 = uint32_t3(glm::lessThan(Vec3ASignedFull, Vec3BSignedFull)); + + expected.mortonSignedLess_small_4 = uint32_t4(glm::lessThan(Vec4ASignedSmall, Vec4BSignedSmall)); + expected.mortonSignedLess_medium_4 = uint32_t4(glm::lessThan(Vec4ASignedMedium, Vec4BSignedMedium)); + expected.mortonSignedLess_full_4 = uint32_t4(glm::lessThan(Vec4ASignedFull, Vec4BSignedFull)); + expected.mortonSignedLess_emulated_4 = uint32_t4(glm::lessThan(Vec4ASignedFull, Vec4BSignedFull)); + + uint16_t castedShift = uint16_t(testInput.shift); + // Left-shift + expected.mortonLeftShift_small_2 = createMortonFromU64Vec(Vec2ASmall << uint16_t(castedShift % smallBits_2)); + expected.mortonLeftShift_medium_2 = createMortonFromU64Vec(Vec2AMedium << uint16_t(castedShift % mediumBits_2)); + expected.mortonLeftShift_full_2 = createMortonFromU64Vec(Vec2AFull << uint32_t(castedShift % fullBits_2)); + expected.mortonLeftShift_emulated_2 = createMortonFromU64Vec(Vec2AFull << uint32_t(castedShift % fullBits_2)); + + expected.mortonLeftShift_small_3 = createMortonFromU64Vec(Vec3ASmall << uint16_t(castedShift % smallBits_3)); + expected.mortonLeftShift_medium_3 = createMortonFromU64Vec(Vec3AMedium << uint16_t(castedShift % mediumBits_3)); + expected.mortonLeftShift_full_3 = createMortonFromU64Vec(Vec3AFull << uint32_t(castedShift % fullBits_3)); + expected.mortonLeftShift_emulated_3 = createMortonFromU64Vec(Vec3AFull << uint32_t(castedShift % fullBits_3)); + + expected.mortonLeftShift_small_4 = createMortonFromU64Vec(Vec4ASmall << uint16_t(castedShift % smallBits_4)); + expected.mortonLeftShift_medium_4 = createMortonFromU64Vec(Vec4AMedium << uint16_t(castedShift % mediumBits_4)); + expected.mortonLeftShift_full_4 = createMortonFromU64Vec(Vec4AFull << uint16_t(castedShift % fullBits_4)); + expected.mortonLeftShift_emulated_4 = createMortonFromU64Vec(Vec4AFull << uint16_t(castedShift % fullBits_4)); + + // Unsigned right-shift + expected.mortonUnsignedRightShift_small_2 = morton::code::create(Vec2ASmall >> uint16_t(castedShift % smallBits_2)); + expected.mortonUnsignedRightShift_medium_2 = morton::code::create(Vec2AMedium >> uint16_t(castedShift % mediumBits_2)); + expected.mortonUnsignedRightShift_full_2 = morton::code::create(Vec2AFull >> uint32_t(castedShift % fullBits_2)); + expected.mortonUnsignedRightShift_emulated_2 = morton::code::create(Vec2AFull >> uint32_t(castedShift % fullBits_2)); + + expected.mortonUnsignedRightShift_small_3 = morton::code::create(Vec3ASmall >> uint16_t(castedShift % smallBits_3)); + expected.mortonUnsignedRightShift_medium_3 = morton::code::create(Vec3AMedium >> uint16_t(castedShift % mediumBits_3)); + expected.mortonUnsignedRightShift_full_3 = morton::code::create(Vec3AFull >> uint32_t(castedShift % fullBits_3)); + expected.mortonUnsignedRightShift_emulated_3 = morton::code::create(Vec3AFull >> uint32_t(castedShift % fullBits_3)); + + expected.mortonUnsignedRightShift_small_4 = morton::code::create(Vec4ASmall >> uint16_t(castedShift % smallBits_4)); + expected.mortonUnsignedRightShift_medium_4 = morton::code::create(Vec4AMedium >> uint16_t(castedShift % mediumBits_4)); + expected.mortonUnsignedRightShift_full_4 = morton::code::create(Vec4AFull >> uint16_t(castedShift % fullBits_4)); + expected.mortonUnsignedRightShift_emulated_4 = morton::code::create(Vec4AFull >> uint16_t(castedShift % fullBits_4)); + + // Signed right-shift + expected.mortonSignedRightShift_small_2 = morton::code::create(Vec2ASignedSmall >> int16_t(castedShift % smallBits_2)); + expected.mortonSignedRightShift_medium_2 = morton::code::create(Vec2ASignedMedium >> int16_t(castedShift % mediumBits_2)); + expected.mortonSignedRightShift_full_2 = morton::code::create(Vec2ASignedFull >> int32_t(castedShift % fullBits_2)); + expected.mortonSignedRightShift_emulated_2 = createMortonFromU64Vec(Vec2ASignedFull >> int32_t(castedShift % fullBits_2)); + + expected.mortonSignedRightShift_small_3 = morton::code::create(Vec3ASignedSmall >> int16_t(castedShift % smallBits_3)); + expected.mortonSignedRightShift_medium_3 = morton::code::create(Vec3ASignedMedium >> int16_t(castedShift % mediumBits_3)); + expected.mortonSignedRightShift_full_3 = morton::code::create(Vec3ASignedFull >> int32_t(castedShift % fullBits_3)); + expected.mortonSignedRightShift_emulated_3 = createMortonFromU64Vec(Vec3ASignedFull >> int32_t(castedShift % fullBits_3)); + + expected.mortonSignedRightShift_small_4 = morton::code::create(Vec4ASignedSmall >> int16_t(castedShift % smallBits_4)); + expected.mortonSignedRightShift_medium_4 = morton::code::create(Vec4ASignedMedium >> int16_t(castedShift % mediumBits_4)); + expected.mortonSignedRightShift_full_4 = morton::code::create(Vec4ASignedFull >> int16_t(castedShift % fullBits_4)); + expected.mortonSignedRightShift_emulated_4 = createMortonFromU64Vec(Vec4ASignedFull >> int16_t(castedShift % fullBits_4)); + } + + return expected; + } + + void verifyTestResults(const TestValues& expectedTestValues, const TestValues& testValues, const size_t testIteration, const uint32_t seed, ITester::TestType testType) override + { + // Some verification is commented out and moved to CTester2 due to bug in dxc. Uncomment them when the bug is fixed. + verifyTestValue("emulatedAnd", expectedTestValues.emulatedAnd, testValues.emulatedAnd, testIteration, seed, testType); + verifyTestValue("emulatedOr", expectedTestValues.emulatedOr, testValues.emulatedOr, testIteration, seed, testType); + verifyTestValue("emulatedXor", expectedTestValues.emulatedXor, testValues.emulatedXor, testIteration, seed, testType); + verifyTestValue("emulatedNot", expectedTestValues.emulatedNot, testValues.emulatedNot, testIteration, seed, testType); + verifyTestValue("emulatedPlus", expectedTestValues.emulatedPlus, testValues.emulatedPlus, testIteration, seed, testType); + verifyTestValue("emulatedMinus", expectedTestValues.emulatedMinus, testValues.emulatedMinus, testIteration, seed, testType); + verifyTestValue("emulatedLess", expectedTestValues.emulatedLess, testValues.emulatedLess, testIteration, seed, testType); + verifyTestValue("emulatedLessEqual", expectedTestValues.emulatedLessEqual, testValues.emulatedLessEqual, testIteration, seed, testType); + verifyTestValue("emulatedGreater", expectedTestValues.emulatedGreater, testValues.emulatedGreater, testIteration, seed, testType); + verifyTestValue("emulatedGreaterEqual", expectedTestValues.emulatedGreaterEqual, testValues.emulatedGreaterEqual, testIteration, seed, testType); + verifyTestValue("emulatedLeftShifted", expectedTestValues.emulatedLeftShifted, testValues.emulatedLeftShifted, testIteration, seed, testType); + verifyTestValue("emulatedUnsignedRightShifted", expectedTestValues.emulatedUnsignedRightShifted, testValues.emulatedUnsignedRightShifted, testIteration, seed, testType); + verifyTestValue("emulatedSignedRightShifted", expectedTestValues.emulatedSignedRightShifted, testValues.emulatedSignedRightShifted, testIteration, seed, testType); + verifyTestValue("emulatedUnaryMinus", expectedTestValues.emulatedUnaryMinus, testValues.emulatedUnaryMinus, testIteration, seed, testType); + + // Morton Plus + verifyTestValue("mortonPlus_small_2", expectedTestValues.mortonPlus_small_2, testValues.mortonPlus_small_2, testIteration, seed, testType); + verifyTestValue("mortonPlus_medium_2", expectedTestValues.mortonPlus_medium_2, testValues.mortonPlus_medium_2, testIteration, seed, testType); + verifyTestValue("mortonPlus_full_2", expectedTestValues.mortonPlus_full_2, testValues.mortonPlus_full_2, testIteration, seed, testType); + verifyTestValue("mortonPlus_emulated_2", expectedTestValues.mortonPlus_emulated_2, testValues.mortonPlus_emulated_2, testIteration, seed, testType); + + verifyTestValue("mortonPlus_small_3", expectedTestValues.mortonPlus_small_3, testValues.mortonPlus_small_3, testIteration, seed, testType); + verifyTestValue("mortonPlus_medium_3", expectedTestValues.mortonPlus_medium_3, testValues.mortonPlus_medium_3, testIteration, seed, testType); + verifyTestValue("mortonPlus_full_3", expectedTestValues.mortonPlus_full_3, testValues.mortonPlus_full_3, testIteration, seed, testType); + verifyTestValue("mortonPlus_emulated_3", expectedTestValues.mortonPlus_emulated_3, testValues.mortonPlus_emulated_3, testIteration, seed, testType); + + verifyTestValue("mortonPlus_small_4", expectedTestValues.mortonPlus_small_4, testValues.mortonPlus_small_4, testIteration, seed, testType); + verifyTestValue("mortonPlus_medium_4", expectedTestValues.mortonPlus_medium_4, testValues.mortonPlus_medium_4, testIteration, seed, testType); + verifyTestValue("mortonPlus_full_4", expectedTestValues.mortonPlus_full_4, testValues.mortonPlus_full_4, testIteration, seed, testType); + verifyTestValue("mortonPlus_emulated_4", expectedTestValues.mortonPlus_emulated_4, testValues.mortonPlus_emulated_4, testIteration, seed, testType); + + // Morton Minus + verifyTestValue("mortonMinus_small_2", expectedTestValues.mortonMinus_small_2, testValues.mortonMinus_small_2, testIteration, seed, testType); + verifyTestValue("mortonMinus_medium_2", expectedTestValues.mortonMinus_medium_2, testValues.mortonMinus_medium_2, testIteration, seed, testType); + verifyTestValue("mortonMinus_full_2", expectedTestValues.mortonMinus_full_2, testValues.mortonMinus_full_2, testIteration, seed, testType); + verifyTestValue("mortonMinus_emulated_2", expectedTestValues.mortonMinus_emulated_2, testValues.mortonMinus_emulated_2, testIteration, seed, testType); + + verifyTestValue("mortonMinus_small_3", expectedTestValues.mortonMinus_small_3, testValues.mortonMinus_small_3, testIteration, seed, testType); + verifyTestValue("mortonMinus_medium_3", expectedTestValues.mortonMinus_medium_3, testValues.mortonMinus_medium_3, testIteration, seed, testType); + verifyTestValue("mortonMinus_full_3", expectedTestValues.mortonMinus_full_3, testValues.mortonMinus_full_3, testIteration, seed, testType); + verifyTestValue("mortonMinus_emulated_3", expectedTestValues.mortonMinus_emulated_3, testValues.mortonMinus_emulated_3, testIteration, seed, testType); + + verifyTestValue("mortonMinus_small_4", expectedTestValues.mortonMinus_small_4, testValues.mortonMinus_small_4, testIteration, seed, testType); + verifyTestValue("mortonMinus_medium_4", expectedTestValues.mortonMinus_medium_4, testValues.mortonMinus_medium_4, testIteration, seed, testType); + verifyTestValue("mortonMinus_full_4", expectedTestValues.mortonMinus_full_4, testValues.mortonMinus_full_4, testIteration, seed, testType); + verifyTestValue("mortonMinus_emulated_4", expectedTestValues.mortonMinus_emulated_4, testValues.mortonMinus_emulated_4, testIteration, seed, testType); + + // Morton coordinate-wise equality + verifyTestValue("mortonEqual_small_2", expectedTestValues.mortonEqual_small_2, testValues.mortonEqual_small_2, testIteration, seed, testType); + verifyTestValue("mortonEqual_medium_2", expectedTestValues.mortonEqual_medium_2, testValues.mortonEqual_medium_2, testIteration, seed, testType); + verifyTestValue("mortonEqual_full_2", expectedTestValues.mortonEqual_full_2, testValues.mortonEqual_full_2, testIteration, seed, testType); + verifyTestValue("mortonEqual_emulated_2", expectedTestValues.mortonEqual_emulated_2, testValues.mortonEqual_emulated_2, testIteration, seed, testType); + + verifyTestValue("mortonEqual_small_3", expectedTestValues.mortonEqual_small_3, testValues.mortonEqual_small_3, testIteration, seed, testType); + verifyTestValue("mortonEqual_medium_3", expectedTestValues.mortonEqual_medium_3, testValues.mortonEqual_medium_3, testIteration, seed, testType); + verifyTestValue("mortonEqual_full_3", expectedTestValues.mortonEqual_full_3, testValues.mortonEqual_full_3, testIteration, seed, testType); + verifyTestValue("mortonEqual_emulated_3", expectedTestValues.mortonEqual_emulated_3, testValues.mortonEqual_emulated_3, testIteration, seed, testType); + + verifyTestValue("mortonEqual_small_4", expectedTestValues.mortonEqual_small_4, testValues.mortonEqual_small_4, testIteration, seed, testType); + verifyTestValue("mortonEqual_medium_4", expectedTestValues.mortonEqual_medium_4, testValues.mortonEqual_medium_4, testIteration, seed, testType); + verifyTestValue("mortonEqual_full_4", expectedTestValues.mortonEqual_full_4, testValues.mortonEqual_full_4, testIteration, seed, testType); + verifyTestValue("mortonEqual_emulated_4", expectedTestValues.mortonEqual_emulated_4, testValues.mortonEqual_emulated_4, testIteration, seed, testType); + + // Morton coordinate-wise unsigned inequality + verifyTestValue("mortonUnsignedLess_small_2", expectedTestValues.mortonUnsignedLess_small_2, testValues.mortonUnsignedLess_small_2, testIteration, seed, testType); + verifyTestValue("mortonUnsignedLess_medium_2", expectedTestValues.mortonUnsignedLess_medium_2, testValues.mortonUnsignedLess_medium_2, testIteration, seed, testType); + verifyTestValue("mortonUnsignedLess_full_2", expectedTestValues.mortonUnsignedLess_full_2, testValues.mortonUnsignedLess_full_2, testIteration, seed, testType); + verifyTestValue("mortonUnsignedLess_emulated_2", expectedTestValues.mortonUnsignedLess_emulated_2, testValues.mortonUnsignedLess_emulated_2, testIteration, seed, testType); + + verifyTestValue("mortonUnsignedLess_small_3", expectedTestValues.mortonUnsignedLess_small_3, testValues.mortonUnsignedLess_small_3, testIteration, seed, testType); + verifyTestValue("mortonUnsignedLess_medium_3", expectedTestValues.mortonUnsignedLess_medium_3, testValues.mortonUnsignedLess_medium_3, testIteration, seed, testType); + verifyTestValue("mortonUnsignedLess_full_3", expectedTestValues.mortonUnsignedLess_full_3, testValues.mortonUnsignedLess_full_3, testIteration, seed, testType); + verifyTestValue("mortonUnsignedLess_emulated_3", expectedTestValues.mortonUnsignedLess_emulated_3, testValues.mortonUnsignedLess_emulated_3, testIteration, seed, testType); + + verifyTestValue("mortonUnsignedLess_small_4", expectedTestValues.mortonUnsignedLess_small_4, testValues.mortonUnsignedLess_small_4, testIteration, seed, testType); + verifyTestValue("mortonUnsignedLess_medium_4", expectedTestValues.mortonUnsignedLess_medium_4, testValues.mortonUnsignedLess_medium_4, testIteration, seed, testType); + verifyTestValue("mortonUnsignedLess_full_4", expectedTestValues.mortonUnsignedLess_full_4, testValues.mortonUnsignedLess_full_4, testIteration, seed, testType); + // verifyTestValue("mortonUnsignedLess_emulated_4", expectedTestValues.mortonUnsignedLess_emulated_4, testValues.mortonUnsignedLess_emulated_4, testIteration, seed, testType); + + // Morton coordinate-wise signed inequality + verifyTestValue("mortonSignedLess_small_2", expectedTestValues.mortonSignedLess_small_2, testValues.mortonSignedLess_small_2, testIteration, seed, testType); + verifyTestValue("mortonSignedLess_medium_2", expectedTestValues.mortonSignedLess_medium_2, testValues.mortonSignedLess_medium_2, testIteration, seed, testType); + verifyTestValue("mortonSignedLess_full_2", expectedTestValues.mortonSignedLess_full_2, testValues.mortonSignedLess_full_2, testIteration, seed, testType); + // verifyTestValue("mortonSignedLess_emulated_2", expectedTestValues.mortonSignedLess_emulated_2, testValues.mortonSignedLess_emulated_2, testIteration, seed, testType); + + verifyTestValue("mortonSignedLess_small_3", expectedTestValues.mortonSignedLess_small_3, testValues.mortonSignedLess_small_3, testIteration, seed, testType); + verifyTestValue("mortonSignedLess_medium_3", expectedTestValues.mortonSignedLess_medium_3, testValues.mortonSignedLess_medium_3, testIteration, seed, testType); + verifyTestValue("mortonSignedLess_full_3", expectedTestValues.mortonSignedLess_full_3, testValues.mortonSignedLess_full_3, testIteration, seed, testType); + // verifyTestValue("mortonSignedLess_emulated_3", expectedTestValues.mortonSignedLess_emulated_3, testValues.mortonSignedLess_emulated_3, testIteration, seed, testType); + + verifyTestValue("mortonSignedLess_small_4", expectedTestValues.mortonSignedLess_small_4, testValues.mortonSignedLess_small_4, testIteration, seed, testType); + verifyTestValue("mortonSignedLess_medium_4", expectedTestValues.mortonSignedLess_medium_4, testValues.mortonSignedLess_medium_4, testIteration, seed, testType); + verifyTestValue("mortonSignedLess_full_4", expectedTestValues.mortonSignedLess_full_4, testValues.mortonSignedLess_full_4, testIteration, seed, testType); + // verifyTestValue("mortonSignedLess_emulated_4", expectedTestValues.mortonSignedLess_emulated_4, testValues.mortonSignedLess_emulated_4, testIteration, seed, testType); + + // Morton left-shift + verifyTestValue("mortonLeftShift_small_2", expectedTestValues.mortonLeftShift_small_2, testValues.mortonLeftShift_small_2, testIteration, seed, testType); + verifyTestValue("mortonLeftShift_medium_2", expectedTestValues.mortonLeftShift_medium_2, testValues.mortonLeftShift_medium_2, testIteration, seed, testType); + verifyTestValue("mortonLeftShift_full_2", expectedTestValues.mortonLeftShift_full_2, testValues.mortonLeftShift_full_2, testIteration, seed, testType); + verifyTestValue("mortonLeftShift_emulated_2", expectedTestValues.mortonLeftShift_emulated_2, testValues.mortonLeftShift_emulated_2, testIteration, seed, testType); + + verifyTestValue("mortonLeftShift_small_3", expectedTestValues.mortonLeftShift_small_3, testValues.mortonLeftShift_small_3, testIteration, seed, testType); + verifyTestValue("mortonLeftShift_medium_3", expectedTestValues.mortonLeftShift_medium_3, testValues.mortonLeftShift_medium_3, testIteration, seed, testType); + verifyTestValue("mortonLeftShift_full_3", expectedTestValues.mortonLeftShift_full_3, testValues.mortonLeftShift_full_3, testIteration, seed, testType); + verifyTestValue("mortonLeftShift_emulated_3", expectedTestValues.mortonLeftShift_emulated_3, testValues.mortonLeftShift_emulated_3, testIteration, seed, testType); + + verifyTestValue("mortonLeftShift_small_4", expectedTestValues.mortonLeftShift_small_4, testValues.mortonLeftShift_small_4, testIteration, seed, testType); + verifyTestValue("mortonLeftShift_medium_4", expectedTestValues.mortonLeftShift_medium_4, testValues.mortonLeftShift_medium_4, testIteration, seed, testType); + verifyTestValue("mortonLeftShift_full_4", expectedTestValues.mortonLeftShift_full_4, testValues.mortonLeftShift_full_4, testIteration, seed, testType); + verifyTestValue("mortonLeftShift_emulated_4", expectedTestValues.mortonLeftShift_emulated_4, testValues.mortonLeftShift_emulated_4, testIteration, seed, testType); + + // Morton unsigned right-shift + verifyTestValue("mortonUnsignedRightShift_small_2", expectedTestValues.mortonUnsignedRightShift_small_2, testValues.mortonUnsignedRightShift_small_2, testIteration, seed, testType); + verifyTestValue("mortonUnsignedRightShift_medium_2", expectedTestValues.mortonUnsignedRightShift_medium_2, testValues.mortonUnsignedRightShift_medium_2, testIteration, seed, testType); + verifyTestValue("mortonUnsignedRightShift_full_2", expectedTestValues.mortonUnsignedRightShift_full_2, testValues.mortonUnsignedRightShift_full_2, testIteration, seed, testType); + verifyTestValue("mortonUnsignedRightShift_emulated_2", expectedTestValues.mortonUnsignedRightShift_emulated_2, testValues.mortonUnsignedRightShift_emulated_2, testIteration, seed, testType); + + verifyTestValue("mortonUnsignedRightShift_small_3", expectedTestValues.mortonUnsignedRightShift_small_3, testValues.mortonUnsignedRightShift_small_3, testIteration, seed, testType); + verifyTestValue("mortonUnsignedRightShift_medium_3", expectedTestValues.mortonUnsignedRightShift_medium_3, testValues.mortonUnsignedRightShift_medium_3, testIteration, seed, testType); + verifyTestValue("mortonUnsignedRightShift_full_3", expectedTestValues.mortonUnsignedRightShift_full_3, testValues.mortonUnsignedRightShift_full_3, testIteration, seed, testType); + verifyTestValue("mortonUnsignedRightShift_emulated_3", expectedTestValues.mortonUnsignedRightShift_emulated_3, testValues.mortonUnsignedRightShift_emulated_3, testIteration, seed, testType); + + verifyTestValue("mortonUnsignedRightShift_small_4", expectedTestValues.mortonUnsignedRightShift_small_4, testValues.mortonUnsignedRightShift_small_4, testIteration, seed, testType); + verifyTestValue("mortonUnsignedRightShift_medium_4", expectedTestValues.mortonUnsignedRightShift_medium_4, testValues.mortonUnsignedRightShift_medium_4, testIteration, seed, testType); + verifyTestValue("mortonUnsignedRightShift_full_4", expectedTestValues.mortonUnsignedRightShift_full_4, testValues.mortonUnsignedRightShift_full_4, testIteration, seed, testType); + verifyTestValue("mortonUnsignedRightShift_emulated_4", expectedTestValues.mortonUnsignedRightShift_emulated_4, testValues.mortonUnsignedRightShift_emulated_4, testIteration, seed, testType); + + // Morton signed right-shift + verifyTestValue("mortonSignedRightShift_small_2", expectedTestValues.mortonSignedRightShift_small_2, testValues.mortonSignedRightShift_small_2, testIteration, seed, testType); + verifyTestValue("mortonSignedRightShift_medium_2", expectedTestValues.mortonSignedRightShift_medium_2, testValues.mortonSignedRightShift_medium_2, testIteration, seed, testType); + verifyTestValue("mortonSignedRightShift_full_2", expectedTestValues.mortonSignedRightShift_full_2, testValues.mortonSignedRightShift_full_2, testIteration, seed, testType); + // verifyTestValue("mortonSignedRightShift_emulated_2", expectedTestValues.mortonSignedRightShift_emulated_2, testValues.mortonSignedRightShift_emulated_2, testIteration, seed, testType); + + verifyTestValue("mortonSignedRightShift_small_3", expectedTestValues.mortonSignedRightShift_small_3, testValues.mortonSignedRightShift_small_3, testIteration, seed, testType); + verifyTestValue("mortonSignedRightShift_medium_3", expectedTestValues.mortonSignedRightShift_medium_3, testValues.mortonSignedRightShift_medium_3, testIteration, seed, testType); + verifyTestValue("mortonSignedRightShift_full_3", expectedTestValues.mortonSignedRightShift_full_3, testValues.mortonSignedRightShift_full_3, testIteration, seed, testType); + //verifyTestValue("mortonSignedRightShift_emulated_3", expectedTestValues.mortonSignedRightShift_emulated_3, testValues.mortonSignedRightShift_emulated_3, testIteration, seed, testType); + + verifyTestValue("mortonSignedRightShift_small_4", expectedTestValues.mortonSignedRightShift_small_4, testValues.mortonSignedRightShift_small_4, testIteration, seed, testType); + verifyTestValue("mortonSignedRightShift_medium_4", expectedTestValues.mortonSignedRightShift_medium_4, testValues.mortonSignedRightShift_medium_4, testIteration, seed, testType); + verifyTestValue("mortonSignedRightShift_full_4", expectedTestValues.mortonSignedRightShift_full_4, testValues.mortonSignedRightShift_full_4, testIteration, seed, testType); + // verifyTestValue("mortonSignedRightShift_emulated_4", expectedTestValues.mortonSignedRightShift_emulated_4, testValues.mortonSignedRightShift_emulated_4, testIteration, seed, testType); + } +}; + +// Some hlsl code will result in compilation error if mixed together due to some bug in dxc. So we separate them into multiple shader compilation and test. +class CTester2 final : public ITester +{ + using base_t = ITester; +public: + CTester2(const uint32_t testBatchCount) + : base_t(testBatchCount) {}; + +private: + InputTestValues generateInputTestValues() override + { + std::uniform_int_distribution intDistribution(uint32_t(0), std::numeric_limits::max()); + std::uniform_int_distribution longDistribution(uint64_t(0), std::numeric_limits::max()); + + // Set input thest values that will be used in both CPU and GPU tests + InputTestValues testInput; + + testInput.generatedA = longDistribution(getRandomEngine()); + testInput.generatedB = longDistribution(getRandomEngine()); + + uint32_t generatedShift = intDistribution(getRandomEngine()) & uint32_t(63); + testInput.shift = generatedShift; + + testInput.coordX = longDistribution(getRandomEngine()); + testInput.coordY = longDistribution(getRandomEngine()); + testInput.coordZ = longDistribution(getRandomEngine()); + testInput.coordW = longDistribution(getRandomEngine()); + + return testInput; + } + + TestValues determineExpectedResults(const InputTestValues& testInput) override + { + // use std library or glm functions to determine expected test values, the output of functions from intrinsics.hlsl will be verified against these values + TestValues expected; + + const uint32_t generatedShift = testInput.shift; + uint64_t2 Vec2A = { testInput.coordX, testInput.coordY }; + uint64_t2 Vec2B = { testInput.coordZ, testInput.coordW }; + + uint64_t3 Vec3A = { testInput.coordX, testInput.coordY, testInput.coordZ }; + uint64_t3 Vec3B = { testInput.coordY, testInput.coordZ, testInput.coordW }; + + uint64_t4 Vec4A = { testInput.coordX, testInput.coordY, testInput.coordZ, testInput.coordW }; + uint64_t4 Vec4B = { testInput.coordY, testInput.coordZ, testInput.coordW, testInput.coordX }; + + uint16_t4 Vec4AFull = createAnyBitIntegerVecFromU64Vec(Vec4A); + uint16_t4 Vec4BFull = createAnyBitIntegerVecFromU64Vec(Vec4B); + + int32_t2 Vec2ASignedFull = createAnyBitIntegerVecFromU64Vec(Vec2A); + int32_t2 Vec2BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec2B); + + int32_t3 Vec3ASignedFull = createAnyBitIntegerVecFromU64Vec(Vec3A); + int32_t3 Vec3BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec3B); + + int16_t4 Vec4ASignedFull = createAnyBitIntegerVecFromU64Vec(Vec4A); + int16_t4 Vec4BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec4B); + + expected.mortonUnsignedLess_emulated_4 = uint32_t4(glm::lessThan(Vec4AFull, Vec4BFull)); + + expected.mortonSignedLess_emulated_2 = uint32_t2(glm::lessThan(Vec2ASignedFull, Vec2BSignedFull)); + expected.mortonSignedLess_emulated_3 = uint32_t3(glm::lessThan(Vec3ASignedFull, Vec3BSignedFull)); + expected.mortonSignedLess_emulated_4 = uint32_t4(glm::lessThan(Vec4ASignedFull, Vec4BSignedFull)); + + uint16_t castedShift = uint16_t(generatedShift); + expected.mortonSignedRightShift_emulated_2 = createMortonFromU64Vec(Vec2ASignedFull >> int32_t(castedShift % fullBits_2)); + expected.mortonSignedRightShift_emulated_3 = createMortonFromU64Vec(Vec3ASignedFull >> int32_t(castedShift % fullBits_3)); + expected.mortonSignedRightShift_emulated_4 = createMortonFromU64Vec(Vec4ASignedFull >> int16_t(castedShift % fullBits_4)); + + return expected; + } + + void verifyTestResults(const TestValues& expectedTestValues, const TestValues& testValues, const size_t testIteration, const uint32_t seed, ITester::TestType testType) override + { + verifyTestValue("mortonUnsignedLess_emulated_4", expectedTestValues.mortonUnsignedLess_emulated_4, testValues.mortonUnsignedLess_emulated_4, testIteration, seed, testType); + + verifyTestValue("mortonSignedLess_emulated_2", expectedTestValues.mortonSignedLess_emulated_2, testValues.mortonSignedLess_emulated_2, testIteration, seed, testType); + verifyTestValue("mortonSignedLess_emulated_3", expectedTestValues.mortonSignedLess_emulated_3, testValues.mortonSignedLess_emulated_3, testIteration, seed, testType); + verifyTestValue("mortonSignedLess_emulated_4", expectedTestValues.mortonSignedLess_emulated_4, testValues.mortonSignedLess_emulated_4, testIteration, seed, testType); + + verifyTestValue("mortonSignedRightShift_emulated_2", expectedTestValues.mortonSignedRightShift_emulated_2, testValues.mortonSignedRightShift_emulated_2, testIteration, seed, testType); + verifyTestValue("mortonSignedRightShift_emulated_3", expectedTestValues.mortonSignedRightShift_emulated_3, testValues.mortonSignedRightShift_emulated_3, testIteration, seed, testType); + verifyTestValue("mortonSignedRightShift_emulated_4", expectedTestValues.mortonSignedRightShift_emulated_4, testValues.mortonSignedRightShift_emulated_4, testIteration, seed, testType); + } +}; +#endif \ No newline at end of file diff --git a/14_Mortons/app_resources/common.hlsl b/14_Mortons/app_resources/common.hlsl new file mode 100644 index 000000000..98e5e1342 --- /dev/null +++ b/14_Mortons/app_resources/common.hlsl @@ -0,0 +1,233 @@ +//// Copyright (C) 2023-2024 - DevSH Graphics Programming Sp. z O.O. +//// This file is part of the "Nabla Engine". +//// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_EXAMPLES_TESTS_12_MORTON_COMMON_INCLUDED_ +#define _NBL_EXAMPLES_TESTS_12_MORTON_COMMON_INCLUDED_ + +#include + +#include + +NBL_CONSTEXPR uint16_t smallBits_2 = 8; +NBL_CONSTEXPR uint16_t mediumBits_2 = 16; +NBL_CONSTEXPR uint16_t fullBits_2 = 32; +NBL_CONSTEXPR uint16_t smallBits_3 = 5; +NBL_CONSTEXPR uint16_t mediumBits_3 = 10; +NBL_CONSTEXPR uint16_t fullBits_3 = 21; +NBL_CONSTEXPR uint16_t smallBits_4 = 4; +NBL_CONSTEXPR uint16_t mediumBits_4 = 8; +NBL_CONSTEXPR uint16_t fullBits_4 = 16; + +using namespace nbl::hlsl; +template +NBL_CONSTEXPR_INLINE_FUNC T createAnyBitIntegerFromU64(uint64_t val) +{ + if(Signed) + { + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint64_t mask = (uint64_t(1) << (Bits - 1)) - 1; + // fill excess bit with one + if (_static_cast(val) < 0) + return _static_cast(val | ~mask); + else + return _static_cast(val & mask); + } else + { + NBL_CONSTEXPR_FUNC_SCOPE_VAR uint64_t mask = (uint64_t(1) << Bits) - 1; + return _static_cast(val & mask); + } +} + +template +NBL_CONSTEXPR_INLINE_FUNC vector createAnyBitIntegerVecFromU64Vec(vector val) +{ + array_get, uint64_t> getter; + array_set, T> setter; + vector output; + NBL_UNROLL + for (uint16_t i = 0; i < D; i++) + { + setter(output, i, createAnyBitIntegerFromU64(getter(val, i))); + } + return output; +} + +template +NBL_CONSTEXPR_INLINE_FUNC morton::code createMortonFromU64Vec(const vector vec) +{ + using morton_code_t = morton::code; + using decode_component_t = typename morton_code_t::decode_component_t; + return morton_code_t::create(createAnyBitIntegerVecFromU64Vec(vec)); +} + +struct InputTestValues +{ + // Both tests + uint32_t shift; + + // Emulated int tests + uint64_t generatedA; + uint64_t generatedB; + + // Morton tests + uint64_t coordX; + uint64_t coordY; + uint64_t coordZ; + uint64_t coordW; +}; + +struct TestValues +{ + // Emulated int tests + emulated_uint64_t emulatedAnd; + emulated_uint64_t emulatedOr; + emulated_uint64_t emulatedXor; + emulated_uint64_t emulatedNot; + emulated_uint64_t emulatedPlus; + emulated_uint64_t emulatedMinus; + emulated_int64_t emulatedUnaryMinus; + // These are bools but stored as uint because you can't store bools, causes a SPIR-V issue + uint32_t emulatedLess; + uint32_t emulatedLessEqual; + uint32_t emulatedGreater; + uint32_t emulatedGreaterEqual; + emulated_uint64_t emulatedLeftShifted; + emulated_uint64_t emulatedUnsignedRightShifted; + emulated_int64_t emulatedSignedRightShifted; + + // Morton tests - for each dimension let's do one small, medium and full-szied (max bits possible) test to cover representation with + // 16, 32 and 64-bit types. Could make it more exhaustive with macros (test all possible bitwidths) + // For emulated mortons, we store only the emulated uint64 representing it, because DXC complains about bitcasts otherwise + + // Plus + morton::code mortonPlus_small_2; + morton::code mortonPlus_medium_2; + morton::code mortonPlus_full_2; + morton::code mortonPlus_emulated_2; + + morton::code mortonPlus_small_3; + morton::code mortonPlus_medium_3; + morton::code mortonPlus_full_3; + morton::code mortonPlus_emulated_3; + + morton::code mortonPlus_small_4; + morton::code mortonPlus_medium_4; + morton::code mortonPlus_full_4; + morton::code mortonPlus_emulated_4; + + // Minus + morton::code mortonMinus_small_2; + morton::code mortonMinus_medium_2; + morton::code mortonMinus_full_2; + morton::code mortonMinus_emulated_2; + + morton::code mortonMinus_small_3; + morton::code mortonMinus_medium_3; + morton::code mortonMinus_full_3; + morton::code mortonMinus_emulated_3; + + morton::code mortonMinus_small_4; + morton::code mortonMinus_medium_4; + morton::code mortonMinus_full_4; + morton::code mortonMinus_emulated_4; + + // Coordinate-wise equality (these are bools) + uint32_t2 mortonEqual_small_2; + uint32_t2 mortonEqual_medium_2; + uint32_t2 mortonEqual_full_2; + uint32_t2 mortonEqual_emulated_2; + + uint32_t3 mortonEqual_small_3; + uint32_t3 mortonEqual_medium_3; + uint32_t3 mortonEqual_full_3; + uint32_t3 mortonEqual_emulated_3; + + uint32_t4 mortonEqual_small_4; + uint32_t4 mortonEqual_medium_4; + uint32_t4 mortonEqual_full_4; + uint32_t4 mortonEqual_emulated_4; + + // Coordinate-wise unsigned inequality (just testing with less, again these are bools) + uint32_t2 mortonUnsignedLess_small_2; + uint32_t2 mortonUnsignedLess_medium_2; + uint32_t2 mortonUnsignedLess_full_2; + uint32_t2 mortonUnsignedLess_emulated_2; + + uint32_t3 mortonUnsignedLess_small_3; + uint32_t3 mortonUnsignedLess_medium_3; + uint32_t3 mortonUnsignedLess_full_3; + uint32_t3 mortonUnsignedLess_emulated_3; + + uint32_t4 mortonUnsignedLess_small_4; + uint32_t4 mortonUnsignedLess_medium_4; + uint32_t4 mortonUnsignedLess_full_4; + uint32_t4 mortonUnsignedLess_emulated_4; + + // Coordinate-wise signed inequality (bools) + uint32_t2 mortonSignedLess_small_2; + uint32_t2 mortonSignedLess_medium_2; + uint32_t2 mortonSignedLess_full_2; + uint32_t2 mortonSignedLess_emulated_2; + + uint32_t3 mortonSignedLess_small_3; + uint32_t3 mortonSignedLess_medium_3; + uint32_t3 mortonSignedLess_full_3; + uint32_t3 mortonSignedLess_emulated_3; + + uint32_t4 mortonSignedLess_small_4; + uint32_t4 mortonSignedLess_medium_4; + uint32_t4 mortonSignedLess_full_4; + uint32_t4 mortonSignedLess_emulated_4; + + // Left-shift + morton::code mortonLeftShift_small_2; + morton::code mortonLeftShift_medium_2; + morton::code mortonLeftShift_full_2; + morton::code mortonLeftShift_emulated_2; + + morton::code mortonLeftShift_small_3; + morton::code mortonLeftShift_medium_3; + morton::code mortonLeftShift_full_3; + morton::code mortonLeftShift_emulated_3; + + morton::code mortonLeftShift_small_4; + morton::code mortonLeftShift_medium_4; + morton::code mortonLeftShift_full_4; + morton::code mortonLeftShift_emulated_4; + + // Unsigned right-shift + morton::code mortonUnsignedRightShift_small_2; + morton::code mortonUnsignedRightShift_medium_2; + morton::code mortonUnsignedRightShift_full_2; + morton::code mortonUnsignedRightShift_emulated_2; + + morton::code mortonUnsignedRightShift_small_3; + morton::code mortonUnsignedRightShift_medium_3; + morton::code mortonUnsignedRightShift_full_3; + morton::code mortonUnsignedRightShift_emulated_3; + + morton::code mortonUnsignedRightShift_small_4; + morton::code mortonUnsignedRightShift_medium_4; + morton::code mortonUnsignedRightShift_full_4; + morton::code mortonUnsignedRightShift_emulated_4; + + // Signed right-shift + morton::code mortonSignedRightShift_small_2; + morton::code mortonSignedRightShift_medium_2; + morton::code mortonSignedRightShift_full_2; + morton::code mortonSignedRightShift_emulated_2; + + morton::code mortonSignedRightShift_small_3; + morton::code mortonSignedRightShift_medium_3; + morton::code mortonSignedRightShift_full_3; + morton::code mortonSignedRightShift_emulated_3; + + morton::code mortonSignedRightShift_small_4; + morton::code mortonSignedRightShift_medium_4; + morton::code mortonSignedRightShift_full_4; + morton::code mortonSignedRightShift_emulated_4; + + +}; + +#endif diff --git a/14_Mortons/app_resources/test.comp.hlsl b/14_Mortons/app_resources/test.comp.hlsl new file mode 100644 index 000000000..2a2c465f4 --- /dev/null +++ b/14_Mortons/app_resources/test.comp.hlsl @@ -0,0 +1,20 @@ +//// Copyright (C) 2023-2024 - 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 +#pragma shader_stage(compute) + +#include "testCommon.hlsl" +#include + +[[vk::binding(0, 0)]] RWStructuredBuffer inputTestValues; +[[vk::binding(1, 0)]] RWStructuredBuffer outputTestValues; + +[numthreads(256, 1, 1)] +[shader("compute")] +void main() +{ + const uint invID = nbl::hlsl::glsl::gl_GlobalInvocationID().x; + TestExecutor executor; + executor(inputTestValues[invID], outputTestValues[invID]); +} + diff --git a/14_Mortons/app_resources/test2.comp.hlsl b/14_Mortons/app_resources/test2.comp.hlsl new file mode 100644 index 000000000..8561faf83 --- /dev/null +++ b/14_Mortons/app_resources/test2.comp.hlsl @@ -0,0 +1,20 @@ +//// Copyright (C) 2023-2024 - 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 +#pragma shader_stage(compute) + +#include "testCommon2.hlsl" +#include + +[[vk::binding(0, 0)]] RWStructuredBuffer inputTestValues; +[[vk::binding(1, 0)]] RWStructuredBuffer outputTestValues; + +[numthreads(256, 1, 1)] +[shader("compute")] +void main() +{ + const uint invID = nbl::hlsl::glsl::gl_GlobalInvocationID().x; + TestExecutor2 executor; + executor(inputTestValues[invID], outputTestValues[invID]); +} + diff --git a/14_Mortons/app_resources/testCommon.hlsl b/14_Mortons/app_resources/testCommon.hlsl new file mode 100644 index 000000000..b285bd8cd --- /dev/null +++ b/14_Mortons/app_resources/testCommon.hlsl @@ -0,0 +1,297 @@ +#include "common.hlsl" + +struct TestExecutor +{ + void operator()(NBL_CONST_REF_ARG(InputTestValues) input, NBL_REF_ARG(TestValues) output) + { + emulated_uint64_t emulatedA = _static_cast(input.generatedA); + emulated_uint64_t emulatedB = _static_cast(input.generatedB); + emulated_int64_t signedEmulatedA = _static_cast(input.generatedA); + + // Emulated int tests + output.emulatedAnd = emulatedA & emulatedB; + output.emulatedOr = emulatedA | emulatedB; + output.emulatedXor = emulatedA ^ emulatedB; + output.emulatedNot = emulatedA.operator~(); + output.emulatedPlus = emulatedA + emulatedB; + output.emulatedMinus = emulatedA - emulatedB; + output.emulatedLess = uint32_t(emulatedA < emulatedB); + output.emulatedLessEqual = uint32_t(emulatedA <= emulatedB); + output.emulatedGreater = uint32_t(emulatedA > emulatedB); + output.emulatedGreaterEqual = uint32_t(emulatedA >= emulatedB); + + left_shift_operator leftShift; + output.emulatedLeftShifted = leftShift(emulatedA, input.shift); + + arithmetic_right_shift_operator unsignedRightShift; + output.emulatedUnsignedRightShifted = unsignedRightShift(emulatedA, input.shift); + + arithmetic_right_shift_operator signedRightShift; + output.emulatedSignedRightShifted = signedRightShift(signedEmulatedA, input.shift); + + output.emulatedUnaryMinus = signedEmulatedA.operator-(); + + // Morton tests + uint64_t2 Vec2A = { input.coordX, input.coordY }; + uint64_t2 Vec2B = { input.coordZ, input.coordW }; + + uint64_t3 Vec3A = { input.coordX, input.coordY, input.coordZ }; + uint64_t3 Vec3B = { input.coordY, input.coordZ, input.coordW }; + + uint64_t4 Vec4A = { input.coordX, input.coordY, input.coordZ, input.coordW }; + uint64_t4 Vec4B = { input.coordY, input.coordZ, input.coordW, input.coordX }; + + uint16_t2 Vec2ASmall = createAnyBitIntegerVecFromU64Vec(Vec2A); + uint16_t2 Vec2BSmall = createAnyBitIntegerVecFromU64Vec(Vec2B); + uint16_t2 Vec2AMedium = createAnyBitIntegerVecFromU64Vec(Vec2A); + uint16_t2 Vec2BMedium = createAnyBitIntegerVecFromU64Vec(Vec2B); + uint32_t2 Vec2AFull = createAnyBitIntegerVecFromU64Vec(Vec2A); + uint32_t2 Vec2BFull = createAnyBitIntegerVecFromU64Vec(Vec2B); + + uint16_t3 Vec3ASmall = createAnyBitIntegerVecFromU64Vec(Vec3A); + uint16_t3 Vec3BSmall = createAnyBitIntegerVecFromU64Vec(Vec3B); + uint16_t3 Vec3AMedium = createAnyBitIntegerVecFromU64Vec(Vec3A); + uint16_t3 Vec3BMedium = createAnyBitIntegerVecFromU64Vec(Vec3B); + uint32_t3 Vec3AFull = createAnyBitIntegerVecFromU64Vec(Vec3A); + uint32_t3 Vec3BFull = createAnyBitIntegerVecFromU64Vec(Vec3B); + + uint16_t4 Vec4ASmall = createAnyBitIntegerVecFromU64Vec(Vec4A); + uint16_t4 Vec4BSmall = createAnyBitIntegerVecFromU64Vec(Vec4B); + uint16_t4 Vec4AMedium = createAnyBitIntegerVecFromU64Vec(Vec4A); + uint16_t4 Vec4BMedium = createAnyBitIntegerVecFromU64Vec(Vec4B); + uint16_t4 Vec4AFull = createAnyBitIntegerVecFromU64Vec(Vec4A); + uint16_t4 Vec4BFull = createAnyBitIntegerVecFromU64Vec(Vec4B); + + int16_t2 Vec2ASignedSmall = createAnyBitIntegerVecFromU64Vec(Vec2A); + int16_t2 Vec2BSignedSmall = createAnyBitIntegerVecFromU64Vec(Vec2B); + int16_t2 Vec2ASignedMedium = createAnyBitIntegerVecFromU64Vec(Vec2A); + int16_t2 Vec2BSignedMedium = createAnyBitIntegerVecFromU64Vec(Vec2B); + int32_t2 Vec2ASignedFull = createAnyBitIntegerVecFromU64Vec(Vec2A); + int32_t2 Vec2BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec2B); + + int16_t3 Vec3ASignedSmall = createAnyBitIntegerVecFromU64Vec(Vec3A); + int16_t3 Vec3BSignedSmall = createAnyBitIntegerVecFromU64Vec(Vec3B); + int16_t3 Vec3ASignedMedium = createAnyBitIntegerVecFromU64Vec(Vec3A); + int16_t3 Vec3BSignedMedium = createAnyBitIntegerVecFromU64Vec(Vec3B); + int32_t3 Vec3ASignedFull = createAnyBitIntegerVecFromU64Vec(Vec3A); + int32_t3 Vec3BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec3B); + + int16_t4 Vec4ASignedSmall = createAnyBitIntegerVecFromU64Vec(Vec4A); + int16_t4 Vec4BSignedSmall = createAnyBitIntegerVecFromU64Vec(Vec4B); + int16_t4 Vec4ASignedMedium = createAnyBitIntegerVecFromU64Vec(Vec4A); + int16_t4 Vec4BSignedMedium = createAnyBitIntegerVecFromU64Vec(Vec4B); + int16_t4 Vec4ASignedFull = createAnyBitIntegerVecFromU64Vec(Vec4A); + int16_t4 Vec4BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec4B); + + morton::code morton_small_2A = createMortonFromU64Vec(Vec2A); + morton::code morton_medium_2A = createMortonFromU64Vec(Vec2A); + morton::code morton_full_2A = createMortonFromU64Vec(Vec2A); + morton::code morton_emulated_2A = createMortonFromU64Vec(Vec2A); + morton::code morton_small_2B = createMortonFromU64Vec(Vec2B); + morton::code morton_medium_2B = createMortonFromU64Vec(Vec2B); + morton::code morton_full_2B = createMortonFromU64Vec(Vec2B); + morton::code morton_emulated_2B = createMortonFromU64Vec(Vec2B); + + morton::code morton_small_3A = createMortonFromU64Vec(Vec3A); + morton::code morton_medium_3A = createMortonFromU64Vec(Vec3A); + morton::code morton_full_3A = createMortonFromU64Vec(Vec3A); + morton::code morton_emulated_3A = createMortonFromU64Vec(Vec3A); + morton::code morton_small_3B = createMortonFromU64Vec(Vec3B); + morton::code morton_medium_3B = createMortonFromU64Vec(Vec3B); + morton::code morton_full_3B = createMortonFromU64Vec(Vec3B); + morton::code morton_emulated_3B = createMortonFromU64Vec(Vec3B); + + morton::code morton_small_4A = createMortonFromU64Vec(Vec4A); + morton::code morton_medium_4A = createMortonFromU64Vec(Vec4A); + morton::code morton_full_4A = createMortonFromU64Vec(Vec4A); + morton::code morton_emulated_4A = createMortonFromU64Vec(Vec4A); + morton::code morton_small_4B = createMortonFromU64Vec(Vec4B); + morton::code morton_medium_4B = createMortonFromU64Vec(Vec4B); + morton::code morton_full_4B = createMortonFromU64Vec(Vec4B); + morton::code morton_emulated_4B = createMortonFromU64Vec(Vec4B); + + morton::code morton_small_2_signed = createMortonFromU64Vec(Vec2A); + morton::code morton_medium_2_signed = createMortonFromU64Vec(Vec2A); + morton::code morton_full_2_signed = createMortonFromU64Vec(Vec2A); + morton::code morton_emulated_2_signed = createMortonFromU64Vec(Vec2A); + + morton::code morton_small_3_signed = createMortonFromU64Vec(Vec3A); + morton::code morton_medium_3_signed = createMortonFromU64Vec(Vec3A); + morton::code morton_full_3_signed = createMortonFromU64Vec(Vec3A); + morton::code morton_emulated_3_signed = createMortonFromU64Vec(Vec3A); + + morton::code morton_small_4_signed = createMortonFromU64Vec(Vec4A); + morton::code morton_medium_4_signed = createMortonFromU64Vec(Vec4A); + morton::code morton_full_4_signed = createMortonFromU64Vec(Vec4A); + morton::code morton_emulated_4_signed = createMortonFromU64Vec(Vec4A); + + // Some test and operation is moved to testCommon2.hlsl due to dxc bug that cause compilation failure. Uncomment when the bug is fixed. + // Plus + output.mortonPlus_small_2 = morton_small_2A + morton_small_2B; + output.mortonPlus_medium_2 = morton_medium_2A + morton_medium_2B; + output.mortonPlus_full_2 = morton_full_2A + morton_full_2B; + output.mortonPlus_emulated_2 = morton_emulated_2A + morton_emulated_2B; + + output.mortonPlus_small_3 = morton_small_3A + morton_small_3B; + output.mortonPlus_medium_3 = morton_medium_3A + morton_medium_3B; + output.mortonPlus_full_3 = morton_full_3A + morton_full_3B; + output.mortonPlus_emulated_3 = morton_emulated_3A + morton_emulated_3B; + + output.mortonPlus_small_4 = morton_small_4A + morton_small_4B; + output.mortonPlus_medium_4 = morton_medium_4A + morton_medium_4B; + output.mortonPlus_full_4 = morton_full_4A + morton_full_4B; + output.mortonPlus_emulated_4 = morton_emulated_4A + morton_emulated_4B; + + // Minus + output.mortonMinus_small_2 = morton_small_2A - morton_small_2B; + output.mortonMinus_medium_2 = morton_medium_2A - morton_medium_2B; + output.mortonMinus_full_2 = morton_full_2A - morton_full_2B; + output.mortonMinus_emulated_2 = morton_emulated_2A - morton_emulated_2B; + + output.mortonMinus_small_3 = morton_small_3A - morton_small_3B; + output.mortonMinus_medium_3 = morton_medium_3A - morton_medium_3B; + output.mortonMinus_full_3 = morton_full_3A - morton_full_3B; + output.mortonMinus_emulated_3 = morton_emulated_3A - morton_emulated_3B; + + output.mortonMinus_small_4 = morton_small_4A - morton_small_4B; + output.mortonMinus_medium_4 = morton_medium_4A - morton_medium_4B; + output.mortonMinus_full_4 = morton_full_4A - morton_full_4B; + output.mortonMinus_emulated_4 = morton_emulated_4A - morton_emulated_4B; + + // Coordinate-wise equality + output.mortonEqual_small_2 = uint32_t2(morton_small_2A.equal(Vec2BSmall)); + output.mortonEqual_medium_2 = uint32_t2(morton_medium_2A.equal(Vec2BMedium)); + output.mortonEqual_full_2 = uint32_t2(morton_full_2A.equal(Vec2BFull)); + output.mortonEqual_emulated_2 = uint32_t2(morton_emulated_2A.equal(Vec2BFull)); + + output.mortonEqual_small_3 = uint32_t3(morton_small_3A.equal(Vec3BSmall)); + output.mortonEqual_medium_3 = uint32_t3(morton_medium_3A.equal(Vec3BMedium)); + output.mortonEqual_full_3 = uint32_t3(morton_full_3A.equal(Vec3BFull)); + output.mortonEqual_emulated_3 = uint32_t3(morton_emulated_3A.equal(Vec3BFull)); + + output.mortonEqual_small_4 = uint32_t4(morton_small_4A.equal(Vec4BSmall)); + output.mortonEqual_medium_4 = uint32_t4(morton_medium_4A.equal(Vec4BMedium)); + output.mortonEqual_full_4 = uint32_t4(morton_full_4A.equal(Vec4BFull)); + output.mortonEqual_emulated_4 = uint32_t4(morton_emulated_4A.equal(Vec4BFull)); + + // Coordinate-wise unsigned inequality (just testing with less) + output.mortonUnsignedLess_small_2 = uint32_t2(morton_small_2A.lessThan(Vec2BSmall)); + output.mortonUnsignedLess_medium_2 = uint32_t2(morton_medium_2A.lessThan(Vec2BMedium)); + output.mortonUnsignedLess_full_2 = uint32_t2(morton_full_2A.lessThan(Vec2BFull)); + output.mortonUnsignedLess_emulated_2 = uint32_t2(morton_emulated_2A.lessThan(Vec2BFull)); + + output.mortonUnsignedLess_small_3 = uint32_t3(morton_small_3A.lessThan(Vec3BSmall)); + output.mortonUnsignedLess_medium_3 = uint32_t3(morton_medium_3A.lessThan(Vec3BMedium)); + output.mortonUnsignedLess_full_3 = uint32_t3(morton_full_3A.lessThan(Vec3BFull)); + output.mortonUnsignedLess_emulated_3 = uint32_t3(morton_emulated_3A.lessThan(Vec3BFull)); + + output.mortonUnsignedLess_small_4 = uint32_t4(morton_small_4A.lessThan(Vec4BSmall)); + output.mortonUnsignedLess_medium_4 = uint32_t4(morton_medium_4A.lessThan(Vec4BMedium)); + output.mortonUnsignedLess_full_4 = uint32_t4(morton_full_4A.lessThan(Vec4BFull)); + // output.mortonUnsignedLess_emulated_4 = uint32_t4(morton_emulated_4A.lessThan(Vec4BFull)); + + // Coordinate-wise signed inequality + output.mortonSignedLess_small_2 = uint32_t2(morton_small_2_signed.lessThan(Vec2BSignedSmall)); + output.mortonSignedLess_medium_2 = uint32_t2(morton_medium_2_signed.lessThan(Vec2BSignedMedium)); + output.mortonSignedLess_full_2 = uint32_t2(morton_full_2_signed.lessThan(Vec2BSignedFull)); + // output.mortonSignedLess_emulated_2 = uint32_t2(morton_emulated_2_signed.lessThan(Vec2BSignedFull)); + + output.mortonSignedLess_small_3 = uint32_t3(morton_small_3_signed.lessThan(Vec3BSignedSmall)); + output.mortonSignedLess_medium_3 = uint32_t3(morton_medium_3_signed.lessThan(Vec3BSignedMedium)); + output.mortonSignedLess_full_3 = uint32_t3(morton_full_3_signed.lessThan(Vec3BSignedFull)); + // output.mortonSignedLess_emulated_3 = uint32_t3(morton_emulated_3_signed.lessThan(Vec3BSignedFull)); + + output.mortonSignedLess_small_4 = uint32_t4(morton_small_4_signed.lessThan(Vec4BSignedSmall)); + output.mortonSignedLess_medium_4 = uint32_t4(morton_medium_4_signed.lessThan(Vec4BSignedMedium)); + output.mortonSignedLess_full_4 = uint32_t4(morton_full_4_signed.lessThan(Vec4BSignedFull)); + // output.mortonSignedLess_emulated_4 = uint32_t4(morton_emulated_4_signed.lessThan(Vec4BSignedFull)); + + // Cast to uint16_t which is what left shift for Mortons expect + uint16_t castedShift = uint16_t(input.shift); + // Each left shift clamps to correct bits so the result kinda makes sense + // Left-shift + left_shift_operator > leftShiftSmall2; + output.mortonLeftShift_small_2 = leftShiftSmall2(morton_small_2A, castedShift % smallBits_2); + left_shift_operator > leftShiftMedium2; + output.mortonLeftShift_medium_2 = leftShiftMedium2(morton_medium_2A, castedShift % mediumBits_2); + left_shift_operator > leftShiftFull2; + output.mortonLeftShift_full_2 = leftShiftFull2(morton_full_2A, castedShift % fullBits_2); + left_shift_operator > leftShiftEmulated2; + output.mortonLeftShift_emulated_2 = leftShiftEmulated2(morton_emulated_2A, castedShift % fullBits_2); + + left_shift_operator > leftShiftSmall3; + output.mortonLeftShift_small_3 = leftShiftSmall3(morton_small_3A, castedShift % smallBits_3); + left_shift_operator > leftShiftMedium3; + output.mortonLeftShift_medium_3 = leftShiftMedium3(morton_medium_3A, castedShift % mediumBits_3); + left_shift_operator > leftShiftFull3; + output.mortonLeftShift_full_3 = leftShiftFull3(morton_full_3A, castedShift % fullBits_3); + left_shift_operator > leftShiftEmulated3; + output.mortonLeftShift_emulated_3 = leftShiftEmulated3(morton_emulated_3A, castedShift % fullBits_3); + + left_shift_operator > leftShiftSmall4; + output.mortonLeftShift_small_4 = leftShiftSmall4(morton_small_4A, castedShift % smallBits_4); + left_shift_operator > leftShiftMedium4; + output.mortonLeftShift_medium_4 = leftShiftMedium4(morton_medium_4A, castedShift % mediumBits_4); + left_shift_operator > leftShiftFull4; + output.mortonLeftShift_full_4 = leftShiftFull4(morton_full_4A, castedShift % fullBits_4); + left_shift_operator > leftShiftEmulated4; + output.mortonLeftShift_emulated_4 = leftShiftEmulated4(morton_emulated_4A, castedShift % fullBits_4); + + // Unsigned right-shift + arithmetic_right_shift_operator > rightShiftSmall2; + output.mortonUnsignedRightShift_small_2 = rightShiftSmall2(morton_small_2A, castedShift % smallBits_2); + arithmetic_right_shift_operator > rightShiftMedium2; + output.mortonUnsignedRightShift_medium_2 = rightShiftMedium2(morton_medium_2A, castedShift % mediumBits_2); + arithmetic_right_shift_operator > rightShiftFull2; + output.mortonUnsignedRightShift_full_2 = rightShiftFull2(morton_full_2A, castedShift % fullBits_2); + arithmetic_right_shift_operator > rightShiftEmulated2; + output.mortonUnsignedRightShift_emulated_2 = rightShiftEmulated2(morton_emulated_2A, castedShift % fullBits_2); + + arithmetic_right_shift_operator > rightShiftSmall3; + output.mortonUnsignedRightShift_small_3 = rightShiftSmall3(morton_small_3A, castedShift % smallBits_3); + arithmetic_right_shift_operator > rightShiftMedium3; + output.mortonUnsignedRightShift_medium_3 = rightShiftMedium3(morton_medium_3A, castedShift % mediumBits_3); + arithmetic_right_shift_operator > rightShiftFull3; + output.mortonUnsignedRightShift_full_3 = rightShiftFull3(morton_full_3A, castedShift % fullBits_3); + arithmetic_right_shift_operator > rightShiftEmulated3; + output.mortonUnsignedRightShift_emulated_3 = rightShiftEmulated3(morton_emulated_3A, castedShift % fullBits_3); + + arithmetic_right_shift_operator > rightShiftSmall4; + output.mortonUnsignedRightShift_small_4 = rightShiftSmall4(morton_small_4A, castedShift % smallBits_4); + arithmetic_right_shift_operator > rightShiftMedium4; + output.mortonUnsignedRightShift_medium_4 = rightShiftMedium4(morton_medium_4A, castedShift % mediumBits_4); + arithmetic_right_shift_operator > rightShiftFull4; + output.mortonUnsignedRightShift_full_4 = rightShiftFull4(morton_full_4A, castedShift % fullBits_4); + arithmetic_right_shift_operator > rightShiftEmulated4; + output.mortonUnsignedRightShift_emulated_4 = rightShiftEmulated4(morton_emulated_4A, castedShift % fullBits_4); + + // Signed right-shift + arithmetic_right_shift_operator > rightShiftSignedSmall2; + output.mortonSignedRightShift_small_2 = rightShiftSignedSmall2(morton_small_2_signed, castedShift % smallBits_2); + arithmetic_right_shift_operator > rightShiftSignedMedium2; + output.mortonSignedRightShift_medium_2 = rightShiftSignedMedium2(morton_medium_2_signed, castedShift % mediumBits_2); + arithmetic_right_shift_operator > rightShiftSignedFull2; + output.mortonSignedRightShift_full_2 = rightShiftSignedFull2(morton_full_2_signed, castedShift % fullBits_2); + // arithmetic_right_shift_operator > rightShiftSignedEmulated2; + // output.mortonSignedRightShift_emulated_2 = rightShiftSignedEmulated2(morton_emulated_2_signed, castedShift % fullBits_2); + + arithmetic_right_shift_operator > rightShiftSignedSmall3; + output.mortonSignedRightShift_small_3 = rightShiftSignedSmall3(morton_small_3_signed, castedShift % smallBits_3); + arithmetic_right_shift_operator > rightShiftSignedMedium3; + output.mortonSignedRightShift_medium_3 = rightShiftSignedMedium3(morton_medium_3_signed, castedShift % mediumBits_3); + arithmetic_right_shift_operator > rightShiftSignedFull3; + output.mortonSignedRightShift_full_3 = rightShiftSignedFull3(morton_full_3_signed, castedShift % fullBits_3); + // arithmetic_right_shift_operator > rightShiftSignedEmulated3; + // output.mortonSignedRightShift_emulated_3 = rightShiftSignedEmulated3(morton_emulated_3_signed, castedShift % fullBits_3); + + arithmetic_right_shift_operator > rightShiftSignedSmall4; + output.mortonSignedRightShift_small_4 = rightShiftSignedSmall4(morton_small_4_signed, castedShift % smallBits_4); + arithmetic_right_shift_operator > rightShiftSignedMedium4; + output.mortonSignedRightShift_medium_4 = rightShiftSignedMedium4(morton_medium_4_signed, castedShift % mediumBits_4); + arithmetic_right_shift_operator > rightShiftSignedFull4; + output.mortonSignedRightShift_full_4 = rightShiftSignedFull4(morton_full_4_signed, castedShift % fullBits_4); + // arithmetic_right_shift_operator > rightShiftSignedEmulated4; + // output.mortonSignedRightShift_emulated_4 = rightShiftSignedEmulated4(morton_emulated_4_signed, castedShift % fullBits_4); + + } +}; diff --git a/14_Mortons/app_resources/testCommon2.hlsl b/14_Mortons/app_resources/testCommon2.hlsl new file mode 100644 index 000000000..5c2a953ac --- /dev/null +++ b/14_Mortons/app_resources/testCommon2.hlsl @@ -0,0 +1,42 @@ +#include "common.hlsl" + +struct TestExecutor2 +{ + void operator()(NBL_CONST_REF_ARG(InputTestValues) input, NBL_REF_ARG(TestValues) output) + { + uint64_t2 Vec2A = { input.coordX, input.coordY }; + uint64_t2 Vec2B = { input.coordZ, input.coordW }; + + uint64_t3 Vec3A = { input.coordX, input.coordY, input.coordZ }; + uint64_t3 Vec3B = { input.coordY, input.coordZ, input.coordW }; + + uint64_t4 Vec4A = { input.coordX, input.coordY, input.coordZ, input.coordW }; + uint64_t4 Vec4B = { input.coordY, input.coordZ, input.coordW, input.coordX }; + + uint16_t4 Vec4BFull = createAnyBitIntegerVecFromU64Vec(Vec4B); + int32_t2 Vec2BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec2B); + int32_t3 Vec3BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec3B); + int16_t4 Vec4BSignedFull = createAnyBitIntegerVecFromU64Vec(Vec4B); + + morton::code morton_emulated_4A = createMortonFromU64Vec(Vec4A); + morton::code morton_emulated_2_signed = createMortonFromU64Vec(Vec2A); + morton::code morton_emulated_3_signed = createMortonFromU64Vec(Vec3A); + morton::code morton_emulated_4_signed = createMortonFromU64Vec(Vec4A); + + + output.mortonUnsignedLess_emulated_4 = uint32_t4(morton_emulated_4A.lessThan(Vec4BFull)); + + output.mortonSignedLess_emulated_2 = uint32_t2(morton_emulated_2_signed.lessThan(Vec2BSignedFull)); + output.mortonSignedLess_emulated_3 = uint32_t3(morton_emulated_3_signed.lessThan(Vec3BSignedFull)); + output.mortonSignedLess_emulated_4 = uint32_t4(morton_emulated_4_signed.lessThan(Vec4BSignedFull)); + + uint16_t castedShift = uint16_t(input.shift); + + arithmetic_right_shift_operator > rightShiftSignedEmulated2; + output.mortonSignedRightShift_emulated_2 = rightShiftSignedEmulated2(morton_emulated_2_signed, castedShift % fullBits_2); + arithmetic_right_shift_operator > rightShiftSignedEmulated3; + output.mortonSignedRightShift_emulated_3 = rightShiftSignedEmulated3(morton_emulated_3_signed, castedShift % fullBits_3); + arithmetic_right_shift_operator > rightShiftSignedEmulated4; + output.mortonSignedRightShift_emulated_4 = rightShiftSignedEmulated4(morton_emulated_4_signed, castedShift % fullBits_4); + } +}; diff --git a/14_Mortons/config.json.template b/14_Mortons/config.json.template new file mode 100644 index 000000000..717d05d53 --- /dev/null +++ b/14_Mortons/config.json.template @@ -0,0 +1,28 @@ +{ + "enableParallelBuild": true, + "threadsPerBuildProcess" : 2, + "isExecuted": false, + "scriptPath": "", + "cmake": { + "configurations": [ "Release", "Debug", "RelWithDebInfo" ], + "buildModes": [], + "requiredOptions": [] + }, + "profiles": [ + { + "backend": "vulkan", // should be none + "platform": "windows", + "buildModes": [], + "runConfiguration": "Release", // we also need to run in Debug nad RWDI because foundational example + "gpuArchitectures": [] + } + ], + "dependencies": [], + "data": [ + { + "dependencies": [], + "command": [""], + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/14_Mortons/main.cpp b/14_Mortons/main.cpp new file mode 100644 index 000000000..df3126359 --- /dev/null +++ b/14_Mortons/main.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2018-2024 - 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 +#include + +#include "nbl/this_example/builtin/build/spirv/keys.hpp" + +#include "nbl/application_templates/MonoDeviceApplication.hpp" +#include "nbl/examples/common/BuiltinResourcesApplication.hpp" + +#include "app_resources/common.hlsl" +#include "CTester.h" + +using namespace nbl::core; +using namespace nbl::hlsl; +using namespace nbl::system; +using namespace nbl::asset; +using namespace nbl::video; +using namespace nbl::examples; +using namespace nbl::application_templates; + +class MortonTest final : public MonoDeviceApplication, public BuiltinResourcesApplication +{ + using device_base_t = MonoDeviceApplication; + using asset_base_t = BuiltinResourcesApplication; +public: + MortonTest(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) : + IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD) { + } + + bool onAppInitialized(smart_refctd_ptr&& system) override + { + // Remember to call the base class initialization! + if (!device_base_t::onAppInitialized(smart_refctd_ptr(system))) + return false; + if (!asset_base_t::onAppInitialized(std::move(system))) + return false; + // Some tests with mortons with emulated uint storage were cut off, it should be fine since each tested on their own produces correct results for each operator + // Blocked by https://github.com/KhronosGroup/SPIRV-Tools/issues/6104 + { + CTester::PipelineSetupData pplnSetupData; + pplnSetupData.device = m_device; + pplnSetupData.api = m_api; + pplnSetupData.assetMgr = m_assetMgr; + pplnSetupData.logger = m_logger; + pplnSetupData.physicalDevice = m_physicalDevice; + pplnSetupData.computeFamilyIndex = getComputeQueue()->getFamilyIndex(); + pplnSetupData.shaderKey = nbl::this_example::builtin::build::get_spirv_key<"test">(m_device.get()); + + CTester mortonTester(4); // 4 * 128 = 512 tests + mortonTester.setupPipeline(pplnSetupData); + mortonTester.performTestsAndVerifyResults("MortonTestLog.txt"); + } + { + CTester2::PipelineSetupData pplnSetupData; + pplnSetupData.device = m_device; + pplnSetupData.api = m_api; + pplnSetupData.assetMgr = m_assetMgr; + pplnSetupData.logger = m_logger; + pplnSetupData.physicalDevice = m_physicalDevice; + pplnSetupData.computeFamilyIndex = getComputeQueue()->getFamilyIndex(); + pplnSetupData.shaderKey = nbl::this_example::builtin::build::get_spirv_key<"test2">(m_device.get()); + + CTester2 mortonTester2(4); + mortonTester2.setupPipeline(reinterpret_cast(pplnSetupData)); + mortonTester2.performTestsAndVerifyResults("MortonTestLog2.txt"); + } + + return true; + } + + void onAppTerminated_impl() override + { + m_device->waitIdle(); + } + + void workLoopBody() override + { + m_keepRunning = false; + } + + bool keepRunning() override + { + return m_keepRunning; + } + + +private: + bool m_keepRunning = true; +}; + +NBL_MAIN_FUNC(MortonTest) \ No newline at end of file diff --git a/14_Mortons/pipeline.groovy b/14_Mortons/pipeline.groovy new file mode 100644 index 000000000..1a7b043a4 --- /dev/null +++ b/14_Mortons/pipeline.groovy @@ -0,0 +1,50 @@ +import org.DevshGraphicsProgramming.Agent +import org.DevshGraphicsProgramming.BuilderInfo +import org.DevshGraphicsProgramming.IBuilder + +class CStreamingAndBufferDeviceAddressBuilder extends IBuilder +{ + public CStreamingAndBufferDeviceAddressBuilder(Agent _agent, _info) + { + super(_agent, _info) + } + + @Override + public boolean prepare(Map axisMapping) + { + return true + } + + @Override + public boolean build(Map axisMapping) + { + IBuilder.CONFIGURATION config = axisMapping.get("CONFIGURATION") + IBuilder.BUILD_TYPE buildType = axisMapping.get("BUILD_TYPE") + + def nameOfBuildDirectory = getNameOfBuildDirectory(buildType) + def nameOfConfig = getNameOfConfig(config) + + agent.execute("cmake --build ${info.rootProjectPath}/${nameOfBuildDirectory}/${info.targetProjectPathRelativeToRoot} --target ${info.targetBaseName} --config ${nameOfConfig} -j12 -v") + + return true + } + + @Override + public boolean test(Map axisMapping) + { + return true + } + + @Override + public boolean install(Map axisMapping) + { + return true + } +} + +def create(Agent _agent, _info) +{ + return new CStreamingAndBufferDeviceAddressBuilder(_agent, _info) +} + +return this \ No newline at end of file diff --git a/22_CppCompat/CIntrinsicsTester.h b/22_CppCompat/CIntrinsicsTester.h index d053977c0..00e343d90 100644 --- a/22_CppCompat/CIntrinsicsTester.h +++ b/22_CppCompat/CIntrinsicsTester.h @@ -5,19 +5,21 @@ #include "nbl/examples/examples.hpp" #include "app_resources/common.hlsl" -#include "ITester.h" using namespace nbl; -class CIntrinsicsTester final : public ITester +class CIntrinsicsTester final : public ITester { + using base_t = ITester; + public: - void performTests() - { - std::random_device rd; - std::mt19937 mt(rd()); + CIntrinsicsTester(const uint32_t testBatchCount) + : base_t(testBatchCount) {}; +private: + IntrinsicsIntputTestValues generateInputTestValues() override + { std::uniform_real_distribution realDistributionNeg(-50.0f, -1.0f); std::uniform_real_distribution realDistributionPos(1.0f, 50.0f); std::uniform_real_distribution realDistributionZeroToOne(0.0f, 1.0f); @@ -26,239 +28,232 @@ class CIntrinsicsTester final : public ITester std::uniform_int_distribution intDistribution(-100, 100); std::uniform_int_distribution uintDistribution(0, 100); - m_logger->log("intrinsics.hlsl TESTS:", system::ILogger::ELL_PERFORMANCE); - for (int i = 0; i < Iterations; ++i) - { - // Set input thest values that will be used in both CPU and GPU tests - IntrinsicsIntputTestValues testInput; - testInput.bitCount = intDistribution(mt); - testInput.crossLhs = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.crossRhs = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.clampVal = realDistribution(mt); - testInput.clampMin = realDistributionNeg(mt); - testInput.clampMax = realDistributionPos(mt); - testInput.length = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.normalize = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.dotLhs = float32_t3(realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt)); - testInput.dotRhs = float32_t3(realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt)); - testInput.determinant = float32_t3x3( - realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt), - realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt), - realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt) - ); - testInput.findMSB = realDistribution(mt); - testInput.findLSB = realDistribution(mt); - testInput.inverse = float32_t3x3( - realDistribution(mt), realDistribution(mt), realDistribution(mt), - realDistribution(mt), realDistribution(mt), realDistribution(mt), - realDistribution(mt), realDistribution(mt), realDistribution(mt) - ); - testInput.transpose = float32_t3x3( - realDistribution(mt), realDistribution(mt), realDistribution(mt), - realDistribution(mt), realDistribution(mt), realDistribution(mt), - realDistribution(mt), realDistribution(mt), realDistribution(mt) - ); - testInput.mulLhs = float32_t3x3( - realDistribution(mt), realDistribution(mt), realDistribution(mt), - realDistribution(mt), realDistribution(mt), realDistribution(mt), - realDistribution(mt), realDistribution(mt), realDistribution(mt) - ); - testInput.mulRhs = float32_t3x3( - realDistribution(mt), realDistribution(mt), realDistribution(mt), - realDistribution(mt), realDistribution(mt), realDistribution(mt), - realDistribution(mt), realDistribution(mt), realDistribution(mt) - ); - testInput.minA = realDistribution(mt); - testInput.minB = realDistribution(mt); - testInput.maxA = realDistribution(mt); - testInput.maxB = realDistribution(mt); - testInput.rsqrt = realDistributionPos(mt); - testInput.bitReverse = realDistribution(mt); - testInput.frac = realDistribution(mt); - testInput.mixX = realDistributionNeg(mt); - testInput.mixY = realDistributionPos(mt); - testInput.mixA = realDistributionZeroToOne(mt); - testInput.sign = realDistribution(mt); - testInput.radians = realDistribution(mt); - testInput.degrees = realDistribution(mt); - testInput.stepEdge = realDistribution(mt); - testInput.stepX = realDistribution(mt); - testInput.smoothStepEdge0 = realDistributionNeg(mt); - testInput.smoothStepEdge1 = realDistributionPos(mt); - testInput.smoothStepX = realDistribution(mt); - - testInput.bitCountVec = int32_t3(intDistribution(mt), intDistribution(mt), intDistribution(mt)); - testInput.clampValVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.clampMinVec = float32_t3(realDistributionNeg(mt), realDistributionNeg(mt), realDistributionNeg(mt)); - testInput.clampMaxVec = float32_t3(realDistributionPos(mt), realDistributionPos(mt), realDistributionPos(mt)); - testInput.findMSBVec = uint32_t3(uintDistribution(mt), uintDistribution(mt), uintDistribution(mt)); - testInput.findLSBVec = uint32_t3(uintDistribution(mt), uintDistribution(mt), uintDistribution(mt)); - testInput.minAVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.minBVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.maxAVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.maxBVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.rsqrtVec = float32_t3(realDistributionPos(mt), realDistributionPos(mt), realDistributionPos(mt)); - testInput.bitReverseVec = uint32_t3(uintDistribution(mt), uintDistribution(mt), uintDistribution(mt)); - testInput.fracVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.mixXVec = float32_t3(realDistributionNeg(mt), realDistributionNeg(mt), realDistributionNeg(mt)); - testInput.mixYVec = float32_t3(realDistributionPos(mt), realDistributionPos(mt), realDistributionPos(mt)); - testInput.mixAVec = float32_t3(realDistributionZeroToOne(mt), realDistributionZeroToOne(mt), realDistributionZeroToOne(mt)); + IntrinsicsIntputTestValues testInput; + testInput.bitCount = intDistribution(getRandomEngine()); + testInput.crossLhs = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.crossRhs = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.clampVal = realDistribution(getRandomEngine()); + testInput.clampMin = realDistributionNeg(getRandomEngine()); + testInput.clampMax = realDistributionPos(getRandomEngine()); + testInput.length = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.normalize = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.dotLhs = float32_t3(realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine())); + testInput.dotRhs = float32_t3(realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine())); + testInput.determinant = float32_t3x3( + realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), + realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), + realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()) + ); + testInput.findMSB = realDistribution(getRandomEngine()); + testInput.findLSB = realDistribution(getRandomEngine()); + testInput.inverse = float32_t3x3( + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()) + ); + testInput.transpose = float32_t3x3( + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()) + ); + testInput.mulLhs = float32_t3x3( + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()) + ); + testInput.mulRhs = float32_t3x3( + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), + realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()) + ); + testInput.minA = realDistribution(getRandomEngine()); + testInput.minB = realDistribution(getRandomEngine()); + testInput.maxA = realDistribution(getRandomEngine()); + testInput.maxB = realDistribution(getRandomEngine()); + testInput.rsqrt = realDistributionPos(getRandomEngine()); + testInput.bitReverse = realDistribution(getRandomEngine()); + testInput.frac = realDistribution(getRandomEngine()); + testInput.mixX = realDistributionNeg(getRandomEngine()); + testInput.mixY = realDistributionPos(getRandomEngine()); + testInput.mixA = realDistributionZeroToOne(getRandomEngine()); + testInput.sign = realDistribution(getRandomEngine()); + testInput.radians = realDistribution(getRandomEngine()); + testInput.degrees = realDistribution(getRandomEngine()); + testInput.stepEdge = realDistribution(getRandomEngine()); + testInput.stepX = realDistribution(getRandomEngine()); + testInput.smoothStepEdge0 = realDistributionNeg(getRandomEngine()); + testInput.smoothStepEdge1 = realDistributionPos(getRandomEngine()); + testInput.smoothStepX = realDistribution(getRandomEngine()); - testInput.signVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.radiansVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.degreesVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.stepEdgeVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.stepXVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.smoothStepEdge0Vec = float32_t3(realDistributionNeg(mt), realDistributionNeg(mt), realDistributionNeg(mt)); - testInput.smoothStepEdge1Vec = float32_t3(realDistributionPos(mt), realDistributionPos(mt), realDistributionPos(mt)); - testInput.smoothStepXVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.faceForwardN = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.faceForwardI = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.faceForwardNref = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.reflectI = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.reflectN = glm::normalize(float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt))); - testInput.refractI = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.refractN = glm::normalize(float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt))); - testInput.refractEta = realDistribution(mt); + testInput.bitCountVec = int32_t3(intDistribution(getRandomEngine()), intDistribution(getRandomEngine()), intDistribution(getRandomEngine())); + testInput.clampValVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.clampMinVec = float32_t3(realDistributionNeg(getRandomEngine()), realDistributionNeg(getRandomEngine()), realDistributionNeg(getRandomEngine())); + testInput.clampMaxVec = float32_t3(realDistributionPos(getRandomEngine()), realDistributionPos(getRandomEngine()), realDistributionPos(getRandomEngine())); + testInput.findMSBVec = uint32_t3(uintDistribution(getRandomEngine()), uintDistribution(getRandomEngine()), uintDistribution(getRandomEngine())); + testInput.findLSBVec = uint32_t3(uintDistribution(getRandomEngine()), uintDistribution(getRandomEngine()), uintDistribution(getRandomEngine())); + testInput.minAVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.minBVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.maxAVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.maxBVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.rsqrtVec = float32_t3(realDistributionPos(getRandomEngine()), realDistributionPos(getRandomEngine()), realDistributionPos(getRandomEngine())); + testInput.bitReverseVec = uint32_t3(uintDistribution(getRandomEngine()), uintDistribution(getRandomEngine()), uintDistribution(getRandomEngine())); + testInput.fracVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.mixXVec = float32_t3(realDistributionNeg(getRandomEngine()), realDistributionNeg(getRandomEngine()), realDistributionNeg(getRandomEngine())); + testInput.mixYVec = float32_t3(realDistributionPos(getRandomEngine()), realDistributionPos(getRandomEngine()), realDistributionPos(getRandomEngine())); + testInput.mixAVec = float32_t3(realDistributionZeroToOne(getRandomEngine()), realDistributionZeroToOne(getRandomEngine()), realDistributionZeroToOne(getRandomEngine())); - // use std library or glm functions to determine expected test values, the output of functions from intrinsics.hlsl will be verified against these values - IntrinsicsTestValues expected; - expected.bitCount = glm::bitCount(testInput.bitCount); - expected.clamp = glm::clamp(testInput.clampVal, testInput.clampMin, testInput.clampMax); - expected.length = glm::length(testInput.length); - expected.dot = glm::dot(testInput.dotLhs, testInput.dotRhs); - expected.determinant = glm::determinant(reinterpret_cast(testInput.determinant)); - expected.findMSB = glm::findMSB(testInput.findMSB); - expected.findLSB = glm::findLSB(testInput.findLSB); - expected.min = glm::min(testInput.minA, testInput.minB); - expected.max = glm::max(testInput.maxA, testInput.maxB); - expected.rsqrt = (1.0f / std::sqrt(testInput.rsqrt)); - expected.mix = std::lerp(testInput.mixX, testInput.mixY, testInput.mixA); - expected.sign = glm::sign(testInput.sign); - expected.radians = glm::radians(testInput.radians); - expected.degrees = glm::degrees(testInput.degrees); - expected.step = glm::step(testInput.stepEdge, testInput.stepX); - expected.smoothStep = glm::smoothstep(testInput.smoothStepEdge0, testInput.smoothStepEdge1, testInput.smoothStepX); + testInput.signVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.radiansVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.degreesVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.stepEdgeVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.stepXVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.smoothStepEdge0Vec = float32_t3(realDistributionNeg(getRandomEngine()), realDistributionNeg(getRandomEngine()), realDistributionNeg(getRandomEngine())); + testInput.smoothStepEdge1Vec = float32_t3(realDistributionPos(getRandomEngine()), realDistributionPos(getRandomEngine()), realDistributionPos(getRandomEngine())); + testInput.smoothStepXVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.faceForwardN = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.faceForwardI = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.faceForwardNref = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.reflectI = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.reflectN = glm::normalize(float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()))); + testInput.refractI = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.refractN = glm::normalize(float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine()))); + testInput.refractEta = realDistribution(getRandomEngine()); - expected.frac = testInput.frac - std::floor(testInput.frac); - expected.bitReverse = glm::bitfieldReverse(testInput.bitReverse); + return testInput; + } - expected.normalize = glm::normalize(testInput.normalize); - expected.cross = glm::cross(testInput.crossLhs, testInput.crossRhs); - expected.bitCountVec = int32_t3(glm::bitCount(testInput.bitCountVec.x), glm::bitCount(testInput.bitCountVec.y), glm::bitCount(testInput.bitCountVec.z)); - expected.clampVec = float32_t3( - glm::clamp(testInput.clampValVec.x, testInput.clampMinVec.x, testInput.clampMaxVec.x), - glm::clamp(testInput.clampValVec.y, testInput.clampMinVec.y, testInput.clampMaxVec.y), - glm::clamp(testInput.clampValVec.z, testInput.clampMinVec.z, testInput.clampMaxVec.z) - ); - expected.findMSBVec = glm::findMSB(testInput.findMSBVec); - expected.findLSBVec = glm::findLSB(testInput.findLSBVec); - expected.minVec = float32_t3( - glm::min(testInput.minAVec.x, testInput.minBVec.x), - glm::min(testInput.minAVec.y, testInput.minBVec.y), - glm::min(testInput.minAVec.z, testInput.minBVec.z) - ); - expected.maxVec = float32_t3( - glm::max(testInput.maxAVec.x, testInput.maxBVec.x), - glm::max(testInput.maxAVec.y, testInput.maxBVec.y), - glm::max(testInput.maxAVec.z, testInput.maxBVec.z) - ); - expected.rsqrtVec = float32_t3(1.0f / std::sqrt(testInput.rsqrtVec.x), 1.0f / std::sqrt(testInput.rsqrtVec.y), 1.0f / std::sqrt(testInput.rsqrtVec.z)); - expected.bitReverseVec = glm::bitfieldReverse(testInput.bitReverseVec); - expected.fracVec = float32_t3( - testInput.fracVec.x - std::floor(testInput.fracVec.x), - testInput.fracVec.y - std::floor(testInput.fracVec.y), - testInput.fracVec.z - std::floor(testInput.fracVec.z)); - expected.mixVec.x = std::lerp(testInput.mixXVec.x, testInput.mixYVec.x, testInput.mixAVec.x); - expected.mixVec.y = std::lerp(testInput.mixXVec.y, testInput.mixYVec.y, testInput.mixAVec.y); - expected.mixVec.z = std::lerp(testInput.mixXVec.z, testInput.mixYVec.z, testInput.mixAVec.z); + IntrinsicsTestValues determineExpectedResults(const IntrinsicsIntputTestValues& testInput) override + { + IntrinsicsTestValues expected; + expected.bitCount = glm::bitCount(testInput.bitCount); + expected.clamp = glm::clamp(testInput.clampVal, testInput.clampMin, testInput.clampMax); + expected.length = glm::length(testInput.length); + expected.dot = glm::dot(testInput.dotLhs, testInput.dotRhs); + expected.determinant = glm::determinant(reinterpret_cast(testInput.determinant)); + expected.findMSB = glm::findMSB(testInput.findMSB); + expected.findLSB = glm::findLSB(testInput.findLSB); + expected.min = glm::min(testInput.minA, testInput.minB); + expected.max = glm::max(testInput.maxA, testInput.maxB); + expected.rsqrt = (1.0f / std::sqrt(testInput.rsqrt)); + expected.mix = std::lerp(testInput.mixX, testInput.mixY, testInput.mixA); + expected.sign = glm::sign(testInput.sign); + expected.radians = glm::radians(testInput.radians); + expected.degrees = glm::degrees(testInput.degrees); + expected.step = glm::step(testInput.stepEdge, testInput.stepX); + expected.smoothStep = glm::smoothstep(testInput.smoothStepEdge0, testInput.smoothStepEdge1, testInput.smoothStepX); - expected.signVec = glm::sign(testInput.signVec); - expected.radiansVec = glm::radians(testInput.radiansVec); - expected.degreesVec = glm::degrees(testInput.degreesVec); - expected.stepVec = glm::step(testInput.stepEdgeVec, testInput.stepXVec); - expected.smoothStepVec = glm::smoothstep(testInput.smoothStepEdge0Vec, testInput.smoothStepEdge1Vec, testInput.smoothStepXVec); - expected.faceForward = glm::faceforward(testInput.faceForwardN, testInput.faceForwardI, testInput.faceForwardNref); - expected.reflect = glm::reflect(testInput.reflectI, testInput.reflectN); - expected.refract = glm::refract(testInput.refractI, testInput.refractN, testInput.refractEta); + expected.addCarry.result = glm::uaddCarry(testInput.addCarryA, testInput.addCarryB, expected.addCarry.carry); + expected.subBorrow.result = glm::usubBorrow(testInput.subBorrowA, testInput.subBorrowB, expected.subBorrow.borrow); - auto mulGlm = nbl::hlsl::mul(testInput.mulLhs, testInput.mulRhs); - expected.mul = reinterpret_cast(mulGlm); - auto transposeGlm = glm::transpose(reinterpret_cast(testInput.transpose)); - expected.transpose = reinterpret_cast(transposeGlm); - auto inverseGlm = glm::inverse(reinterpret_cast(testInput.inverse)); - expected.inverse = reinterpret_cast(inverseGlm); + expected.frac = testInput.frac - std::floor(testInput.frac); + expected.bitReverse = glm::bitfieldReverse(testInput.bitReverse); - performCpuTests(testInput, expected); - performGpuTests(testInput, expected); - } - m_logger->log("intrinsics.hlsl TESTS DONE.", system::ILogger::ELL_PERFORMANCE); - } + expected.normalize = glm::normalize(testInput.normalize); + expected.cross = glm::cross(testInput.crossLhs, testInput.crossRhs); + expected.bitCountVec = int32_t3(glm::bitCount(testInput.bitCountVec.x), glm::bitCount(testInput.bitCountVec.y), glm::bitCount(testInput.bitCountVec.z)); + expected.clampVec = float32_t3( + glm::clamp(testInput.clampValVec.x, testInput.clampMinVec.x, testInput.clampMaxVec.x), + glm::clamp(testInput.clampValVec.y, testInput.clampMinVec.y, testInput.clampMaxVec.y), + glm::clamp(testInput.clampValVec.z, testInput.clampMinVec.z, testInput.clampMaxVec.z) + ); + expected.findMSBVec = glm::findMSB(testInput.findMSBVec); + expected.findLSBVec = glm::findLSB(testInput.findLSBVec); + expected.minVec = float32_t3( + glm::min(testInput.minAVec.x, testInput.minBVec.x), + glm::min(testInput.minAVec.y, testInput.minBVec.y), + glm::min(testInput.minAVec.z, testInput.minBVec.z) + ); + expected.maxVec = float32_t3( + glm::max(testInput.maxAVec.x, testInput.maxBVec.x), + glm::max(testInput.maxAVec.y, testInput.maxBVec.y), + glm::max(testInput.maxAVec.z, testInput.maxBVec.z) + ); + expected.rsqrtVec = float32_t3(1.0f / std::sqrt(testInput.rsqrtVec.x), 1.0f / std::sqrt(testInput.rsqrtVec.y), 1.0f / std::sqrt(testInput.rsqrtVec.z)); + expected.bitReverseVec = glm::bitfieldReverse(testInput.bitReverseVec); + expected.fracVec = float32_t3( + testInput.fracVec.x - std::floor(testInput.fracVec.x), + testInput.fracVec.y - std::floor(testInput.fracVec.y), + testInput.fracVec.z - std::floor(testInput.fracVec.z)); + expected.mixVec.x = std::lerp(testInput.mixXVec.x, testInput.mixYVec.x, testInput.mixAVec.x); + expected.mixVec.y = std::lerp(testInput.mixXVec.y, testInput.mixYVec.y, testInput.mixAVec.y); + expected.mixVec.z = std::lerp(testInput.mixXVec.z, testInput.mixYVec.z, testInput.mixAVec.z); -private: - inline static constexpr int Iterations = 100u; + expected.signVec = glm::sign(testInput.signVec); + expected.radiansVec = glm::radians(testInput.radiansVec); + expected.degreesVec = glm::degrees(testInput.degreesVec); + expected.stepVec = glm::step(testInput.stepEdgeVec, testInput.stepXVec); + expected.smoothStepVec = glm::smoothstep(testInput.smoothStepEdge0Vec, testInput.smoothStepEdge1Vec, testInput.smoothStepXVec); + expected.faceForward = glm::faceforward(testInput.faceForwardN, testInput.faceForwardI, testInput.faceForwardNref); + expected.reflect = glm::reflect(testInput.reflectI, testInput.reflectN); + expected.refract = glm::refract(testInput.refractI, testInput.refractN, testInput.refractEta); - void performCpuTests(const IntrinsicsIntputTestValues& commonTestInputValues, const IntrinsicsTestValues& expectedTestValues) - { - IntrinsicsTestValues cpuTestValues; - cpuTestValues.fillTestValues(commonTestInputValues); - verifyTestValues(expectedTestValues, cpuTestValues, ITester::TestType::CPU); + expected.addCarryVec.result = glm::uaddCarry(testInput.addCarryAVec, testInput.addCarryBVec, expected.addCarryVec.carry); + expected.subBorrowVec.result = glm::usubBorrow(testInput.subBorrowAVec, testInput.subBorrowBVec, expected.subBorrowVec.borrow); - } + auto mulGlm = nbl::hlsl::mul(testInput.mulLhs, testInput.mulRhs); + expected.mul = reinterpret_cast(mulGlm); + auto transposeGlm = glm::transpose(reinterpret_cast(testInput.transpose)); + expected.transpose = reinterpret_cast(transposeGlm); + auto inverseGlm = glm::inverse(reinterpret_cast(testInput.inverse)); + expected.inverse = reinterpret_cast(inverseGlm); - void performGpuTests(const IntrinsicsIntputTestValues& commonTestInputValues, const IntrinsicsTestValues& expectedTestValues) - { - IntrinsicsTestValues gpuTestValues; - gpuTestValues = dispatch(commonTestInputValues); - verifyTestValues(expectedTestValues, gpuTestValues, ITester::TestType::GPU); + return expected; } - void verifyTestValues(const IntrinsicsTestValues& expectedTestValues, const IntrinsicsTestValues& testValues, ITester::TestType testType) + void verifyTestResults(const IntrinsicsTestValues& expectedTestValues, const IntrinsicsTestValues& testValues, const size_t testIteration, const uint32_t seed, TestType testType) override { - verifyTestValue("bitCount", expectedTestValues.bitCount, testValues.bitCount, testType); - verifyTestValue("clamp", expectedTestValues.clamp, testValues.clamp, testType); - verifyTestValue("length", expectedTestValues.length, testValues.length, testType); - verifyTestValue("dot", expectedTestValues.dot, testValues.dot, testType); - verifyTestValue("determinant", expectedTestValues.determinant, testValues.determinant, testType); - verifyTestValue("findMSB", expectedTestValues.findMSB, testValues.findMSB, testType); - verifyTestValue("findLSB", expectedTestValues.findLSB, testValues.findLSB, testType); - verifyTestValue("min", expectedTestValues.min, testValues.min, testType); - verifyTestValue("max", expectedTestValues.max, testValues.max, testType); - verifyTestValue("rsqrt", expectedTestValues.rsqrt, testValues.rsqrt, testType); - verifyTestValue("frac", expectedTestValues.frac, testValues.frac, testType); - verifyTestValue("bitReverse", expectedTestValues.bitReverse, testValues.bitReverse, testType); - verifyTestValue("mix", expectedTestValues.mix, testValues.mix, testType); - verifyTestValue("sign", expectedTestValues.sign, testValues.sign, testType); - verifyTestValue("radians", expectedTestValues.radians, testValues.radians, testType); - verifyTestValue("degrees", expectedTestValues.degrees, testValues.degrees, testType); - verifyTestValue("step", expectedTestValues.step, testValues.step, testType); - verifyTestValue("smoothStep", expectedTestValues.smoothStep, testValues.smoothStep, testType); + verifyTestValue("bitCount", expectedTestValues.bitCount, testValues.bitCount, testIteration, seed, testType); + verifyTestValue("clamp", expectedTestValues.clamp, testValues.clamp, testIteration, seed, testType); + verifyTestValue("length", expectedTestValues.length, testValues.length, testIteration, seed, testType, 0.0001); + verifyTestValue("dot", expectedTestValues.dot, testValues.dot, testIteration, seed, testType, 0.00001); + verifyTestValue("determinant", expectedTestValues.determinant, testValues.determinant, testIteration, seed, testType); + verifyTestValue("findMSB", expectedTestValues.findMSB, testValues.findMSB, testIteration, seed, testType); + verifyTestValue("findLSB", expectedTestValues.findLSB, testValues.findLSB, testIteration, seed, testType); + verifyTestValue("min", expectedTestValues.min, testValues.min, testIteration, seed, testType); + verifyTestValue("max", expectedTestValues.max, testValues.max, testIteration, seed, testType); + verifyTestValue("rsqrt", expectedTestValues.rsqrt, testValues.rsqrt, testIteration, seed, testType); + verifyTestValue("frac", expectedTestValues.frac, testValues.frac, testIteration, seed, testType); + verifyTestValue("bitReverse", expectedTestValues.bitReverse, testValues.bitReverse, testIteration, seed, testType); + verifyTestValue("mix", expectedTestValues.mix, testValues.mix, testIteration, seed, testType); + verifyTestValue("sign", expectedTestValues.sign, testValues.sign, testIteration, seed, testType); + verifyTestValue("radians", expectedTestValues.radians, testValues.radians, testIteration, seed, testType, 0.00001); + verifyTestValue("degrees", expectedTestValues.degrees, testValues.degrees, testIteration, seed, testType, 0.001); + verifyTestValue("step", expectedTestValues.step, testValues.step, testIteration, seed, testType); + verifyTestValue("smoothStep", expectedTestValues.smoothStep, testValues.smoothStep, testIteration, seed, testType); + verifyTestValue("addCarryResult", expectedTestValues.addCarry.result, testValues.addCarry.result, testIteration, seed, testType); + verifyTestValue("addCarryCarry", expectedTestValues.addCarry.carry, testValues.addCarry.carry, testIteration, seed, testType); + verifyTestValue("subBorrowResult", expectedTestValues.subBorrow.result, testValues.subBorrow.result, testIteration, seed, testType); + verifyTestValue("subBorrowBorrow", expectedTestValues.subBorrow.borrow, testValues.subBorrow.borrow, testIteration, seed, testType); - verifyTestVector3dValue("normalize", expectedTestValues.normalize, testValues.normalize, testType); - verifyTestVector3dValue("cross", expectedTestValues.cross, testValues.cross, testType); - verifyTestVector3dValue("bitCountVec", expectedTestValues.bitCountVec, testValues.bitCountVec, testType); - verifyTestVector3dValue("clampVec", expectedTestValues.clampVec, testValues.clampVec, testType); - verifyTestVector3dValue("findMSBVec", expectedTestValues.findMSBVec, testValues.findMSBVec, testType); - verifyTestVector3dValue("findLSBVec", expectedTestValues.findLSBVec, testValues.findLSBVec, testType); - verifyTestVector3dValue("minVec", expectedTestValues.minVec, testValues.minVec, testType); - verifyTestVector3dValue("maxVec", expectedTestValues.maxVec, testValues.maxVec, testType); - verifyTestVector3dValue("rsqrtVec", expectedTestValues.rsqrtVec, testValues.rsqrtVec, testType); - verifyTestVector3dValue("bitReverseVec", expectedTestValues.bitReverseVec, testValues.bitReverseVec, testType); - verifyTestVector3dValue("fracVec", expectedTestValues.fracVec, testValues.fracVec, testType); - verifyTestVector3dValue("mixVec", expectedTestValues.mixVec, testValues.mixVec, testType); + verifyTestValue("normalize", expectedTestValues.normalize, testValues.normalize, testIteration, seed, testType, 0.000001); + verifyTestValue("cross", expectedTestValues.cross, testValues.cross, testIteration, seed, testType); + verifyTestValue("bitCountVec", expectedTestValues.bitCountVec, testValues.bitCountVec, testIteration, seed, testType); + verifyTestValue("clampVec", expectedTestValues.clampVec, testValues.clampVec, testIteration, seed, testType); + verifyTestValue("findMSBVec", expectedTestValues.findMSBVec, testValues.findMSBVec, testIteration, seed, testType); + verifyTestValue("findLSBVec", expectedTestValues.findLSBVec, testValues.findLSBVec, testIteration, seed, testType); + verifyTestValue("minVec", expectedTestValues.minVec, testValues.minVec, testIteration, seed, testType); + verifyTestValue("maxVec", expectedTestValues.maxVec, testValues.maxVec, testIteration, seed, testType); + verifyTestValue("rsqrtVec", expectedTestValues.rsqrtVec, testValues.rsqrtVec, testIteration, seed, testType); + verifyTestValue("bitReverseVec", expectedTestValues.bitReverseVec, testValues.bitReverseVec, testIteration, seed, testType); + verifyTestValue("fracVec", expectedTestValues.fracVec, testValues.fracVec, testIteration, seed, testType); + verifyTestValue("mixVec", expectedTestValues.mixVec, testValues.mixVec, testIteration, seed, testType); - verifyTestVector3dValue("signVec", expectedTestValues.signVec, testValues.signVec, testType); - verifyTestVector3dValue("radiansVec", expectedTestValues.radiansVec, testValues.radiansVec, testType); - verifyTestVector3dValue("degreesVec", expectedTestValues.degreesVec, testValues.degreesVec, testType); - verifyTestVector3dValue("stepVec", expectedTestValues.stepVec, testValues.stepVec, testType); - verifyTestVector3dValue("smoothStepVec", expectedTestValues.smoothStepVec, testValues.smoothStepVec, testType); - verifyTestVector3dValue("faceForward", expectedTestValues.faceForward, testValues.faceForward, testType); - verifyTestVector3dValue("reflect", expectedTestValues.reflect, testValues.reflect, testType); - verifyTestVector3dValue("refract", expectedTestValues.refract, testValues.refract, testType); + verifyTestValue("signVec", expectedTestValues.signVec, testValues.signVec, testIteration, seed, testType); + verifyTestValue("radiansVec", expectedTestValues.radiansVec, testValues.radiansVec, testIteration, seed, testType, 0.00001); + verifyTestValue("degreesVec", expectedTestValues.degreesVec, testValues.degreesVec, testIteration, seed, testType, 0.001); + verifyTestValue("stepVec", expectedTestValues.stepVec, testValues.stepVec, testIteration, seed, testType); + verifyTestValue("smoothStepVec", expectedTestValues.smoothStepVec, testValues.smoothStepVec, testIteration, seed, testType); + verifyTestValue("faceForward", expectedTestValues.faceForward, testValues.faceForward, testIteration, seed, testType); + verifyTestValue("reflect", expectedTestValues.reflect, testValues.reflect, testIteration, seed, testType, 0.001); + verifyTestValue("refract", expectedTestValues.refract, testValues.refract, testIteration, seed, testType, 0.01); + verifyTestValue("addCarryVecResult", expectedTestValues.addCarryVec.result, testValues.addCarryVec.result, testIteration, seed, testType); + verifyTestValue("addCarryVecCarry", expectedTestValues.addCarryVec.carry, testValues.addCarryVec.carry, testIteration, seed, testType); + verifyTestValue("subBorrowVecResult", expectedTestValues.subBorrowVec.result, testValues.subBorrowVec.result, testIteration, seed, testType); + verifyTestValue("subBorrowVecBorrow", expectedTestValues.subBorrowVec.borrow, testValues.subBorrowVec.borrow, testIteration, seed, testType); - verifyTestMatrix3x3Value("mul", expectedTestValues.mul, testValues.mul, testType); - verifyTestMatrix3x3Value("transpose", expectedTestValues.transpose, testValues.transpose, testType); - verifyTestMatrix3x3Value("inverse", expectedTestValues.inverse, testValues.inverse, testType); + verifyTestValue("mul", expectedTestValues.mul, testValues.mul, testIteration, seed, testType); + verifyTestValue("transpose", expectedTestValues.transpose, testValues.transpose, testIteration, seed, testType); + verifyTestValue("inverse", expectedTestValues.inverse, testValues.inverse, testIteration, seed, testType); } }; diff --git a/22_CppCompat/CMakeLists.txt b/22_CppCompat/CMakeLists.txt index b7e52875d..86a1c34fc 100644 --- a/22_CppCompat/CMakeLists.txt +++ b/22_CppCompat/CMakeLists.txt @@ -21,4 +21,65 @@ if(NBL_EMBED_BUILTIN_RESOURCES) ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) -endif() \ No newline at end of file +endif() + +if(MSVC) + target_compile_options("${EXECUTABLE_NAME}" PUBLIC "/fp:strict") +else() + target_compile_options("${EXECUTABLE_NAME}" PUBLIC -ffloat-store -frounding-math -fsignaling-nans -ftrapping-math) +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/common.hlsl + app_resources/test.comp.hlsl + app_resources/intrinsicsTest.comp.hlsl + app_resources/tgmathTest.comp.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/test.comp.hlsl", + "KEY": "test", + }, + { + "INPUT": "app_resources/intrinsicsTest.comp.hlsl", + "KEY": "intrinsicsTest", + }, + { + "INPUT": "app_resources/tgmathTest.comp.hlsl", + "KEY": "tgmathTest", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) \ No newline at end of file diff --git a/22_CppCompat/CTgmathTester.h b/22_CppCompat/CTgmathTester.h index 63b0e483e..ff6861409 100644 --- a/22_CppCompat/CTgmathTester.h +++ b/22_CppCompat/CTgmathTester.h @@ -3,358 +3,337 @@ #include "nbl/examples/examples.hpp" - #include "app_resources/common.hlsl" -#include "ITester.h" - +#include "nbl/examples/Tester/ITester.h" using namespace nbl; -class CTgmathTester final : public ITester +class CTgmathTester final : public ITester { + using base_t = ITester; + public: - void performTests() - { - std::random_device rd; - std::mt19937 mt(rd()); + CTgmathTester(const uint32_t testBatchCount) + : base_t(testBatchCount) {}; - std::uniform_real_distribution realDistributionNeg(-50.0f, -1.0f); - std::uniform_real_distribution realDistributionPos(1.0f, 50.0f); +private: + TgmathIntputTestValues generateInputTestValues() override + { std::uniform_real_distribution realDistribution(-100.0f, 100.0f); std::uniform_real_distribution realDistributionSmall(1.0f, 4.0f); std::uniform_int_distribution intDistribution(-100, 100); std::uniform_int_distribution coinFlipDistribution(0, 1); - m_logger->log("tgmath.hlsl TESTS:", system::ILogger::ELL_PERFORMANCE); - for (int i = 0; i < Iterations; ++i) - { - // Set input thest values that will be used in both CPU and GPU tests - TgmathIntputTestValues testInput; - testInput.floor = realDistribution(mt); - testInput.isnan = coinFlipDistribution(mt) ? realDistribution(mt) : std::numeric_limits::quiet_NaN(); - testInput.isinf = coinFlipDistribution(mt) ? realDistribution(mt) : std::numeric_limits::infinity(); - testInput.powX = realDistributionSmall(mt); - testInput.powY = realDistributionSmall(mt); - testInput.exp = realDistributionSmall(mt); - testInput.exp2 = realDistributionSmall(mt); - testInput.log = realDistribution(mt); - testInput.log2 = realDistribution(mt); - testInput.absF = realDistribution(mt); - testInput.absI = intDistribution(mt); - testInput.sqrt = realDistribution(mt); - testInput.sin = realDistribution(mt); - testInput.cos = realDistribution(mt); - testInput.tan = realDistribution(mt); - testInput.asin = realDistribution(mt); - testInput.atan = realDistribution(mt); - testInput.sinh = realDistribution(mt); - testInput.cosh = realDistribution(mt); - testInput.tanh = realDistribution(mt); - testInput.asinh = realDistribution(mt); - testInput.acosh = realDistribution(mt); - testInput.atanh = realDistribution(mt); - testInput.atan2X = realDistribution(mt); - testInput.atan2Y = realDistribution(mt); - testInput.acos = realDistribution(mt); - testInput.modf = realDistribution(mt); - testInput.round = realDistribution(mt); - testInput.roundEven = coinFlipDistribution(mt) ? realDistributionSmall(mt) : (static_cast(intDistribution(mt) / 2) + 0.5f); - testInput.trunc = realDistribution(mt); - testInput.ceil = realDistribution(mt); - testInput.fmaX = realDistribution(mt); - testInput.fmaY = realDistribution(mt); - testInput.fmaZ = realDistribution(mt); - testInput.ldexpArg = realDistributionSmall(mt); - testInput.ldexpExp = intDistribution(mt); - testInput.erf = realDistribution(mt); - testInput.erfInv = realDistribution(mt); - - testInput.floorVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.isnanVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.isinfVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.powXVec = float32_t3(realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt)); - testInput.powYVec = float32_t3(realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt)); - testInput.expVec = float32_t3(realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt)); - testInput.exp2Vec = float32_t3(realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt)); - testInput.logVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.log2Vec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.absFVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.absIVec = int32_t3(intDistribution(mt), intDistribution(mt), intDistribution(mt)); - testInput.sqrtVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.sinVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.cosVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.tanVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.asinVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.atanVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.sinhVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.coshVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.tanhVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.asinhVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.acoshVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.atanhVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.atan2XVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.atan2YVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.acosVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.modfVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.ldexpArgVec = float32_t3(realDistributionSmall(mt), realDistributionSmall(mt), realDistributionSmall(mt)); - testInput.ldexpExpVec = float32_t3(intDistribution(mt), intDistribution(mt), intDistribution(mt)); - testInput.erfVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.erfInvVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - - testInput.modfStruct = realDistribution(mt); - testInput.modfStructVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); - testInput.frexpStruct = realDistribution(mt); - testInput.frexpStructVec = float32_t3(realDistribution(mt), realDistribution(mt), realDistribution(mt)); + TgmathIntputTestValues testInput; + testInput.floor = realDistribution(getRandomEngine()); + testInput.isnan = coinFlipDistribution(getRandomEngine()) ? realDistribution(getRandomEngine()) : std::numeric_limits::quiet_NaN(); + testInput.isinf = coinFlipDistribution(getRandomEngine()) ? realDistribution(getRandomEngine()) : std::numeric_limits::infinity(); + testInput.powX = realDistributionSmall(getRandomEngine()); + testInput.powY = realDistributionSmall(getRandomEngine()); + testInput.exp = realDistributionSmall(getRandomEngine()); + testInput.exp2 = realDistributionSmall(getRandomEngine()); + testInput.log = realDistribution(getRandomEngine()); + testInput.log2 = realDistribution(getRandomEngine()); + testInput.absF = realDistribution(getRandomEngine()); + testInput.absI = intDistribution(getRandomEngine()); + testInput.sqrt = realDistribution(getRandomEngine()); + testInput.sin = realDistribution(getRandomEngine()); + testInput.cos = realDistribution(getRandomEngine()); + testInput.tan = realDistribution(getRandomEngine()); + testInput.asin = realDistribution(getRandomEngine()); + testInput.atan = realDistribution(getRandomEngine()); + testInput.sinh = realDistribution(getRandomEngine()); + testInput.cosh = realDistribution(getRandomEngine()); + testInput.tanh = realDistribution(getRandomEngine()); + testInput.asinh = realDistribution(getRandomEngine()); + testInput.acosh = realDistribution(getRandomEngine()); + testInput.atanh = realDistribution(getRandomEngine()); + testInput.atan2X = realDistribution(getRandomEngine()); + testInput.atan2Y = realDistribution(getRandomEngine()); + testInput.acos = realDistribution(getRandomEngine()); + testInput.modf = realDistribution(getRandomEngine()); + testInput.round = realDistribution(getRandomEngine()); + testInput.roundEven = coinFlipDistribution(getRandomEngine()) ? realDistributionSmall(getRandomEngine()) : (static_cast(intDistribution(getRandomEngine()) / 2) + 0.5f); + testInput.trunc = realDistribution(getRandomEngine()); + testInput.ceil = realDistribution(getRandomEngine()); + testInput.fmaX = realDistribution(getRandomEngine()); + testInput.fmaY = realDistribution(getRandomEngine()); + testInput.fmaZ = realDistribution(getRandomEngine()); + testInput.ldexpArg = realDistributionSmall(getRandomEngine()); + testInput.ldexpExp = intDistribution(getRandomEngine()); + testInput.erf = realDistribution(getRandomEngine()); + testInput.erfInv = realDistribution(getRandomEngine()); + + testInput.floorVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.isnanVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.isinfVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.powXVec = float32_t3(realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine())); + testInput.powYVec = float32_t3(realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine())); + testInput.expVec = float32_t3(realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine())); + testInput.exp2Vec = float32_t3(realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine())); + testInput.logVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.log2Vec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.absFVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.absIVec = int32_t3(intDistribution(getRandomEngine()), intDistribution(getRandomEngine()), intDistribution(getRandomEngine())); + testInput.sqrtVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.sinVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.cosVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.tanVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.asinVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.atanVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.sinhVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.coshVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.tanhVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.asinhVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.acoshVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.atanhVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.atan2XVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.atan2YVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.acosVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.modfVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.ldexpArgVec = float32_t3(realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine()), realDistributionSmall(getRandomEngine())); + testInput.ldexpExpVec = float32_t3(intDistribution(getRandomEngine()), intDistribution(getRandomEngine()), intDistribution(getRandomEngine())); + testInput.erfVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.erfInvVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + + testInput.modfStruct = realDistribution(getRandomEngine()); + testInput.modfStructVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + testInput.frexpStruct = realDistribution(getRandomEngine()); + testInput.frexpStructVec = float32_t3(realDistribution(getRandomEngine()), realDistribution(getRandomEngine()), realDistribution(getRandomEngine())); + + return testInput; + } - // use std library functions to determine expected test values, the output of functions from tgmath.hlsl will be verified against these values - TgmathTestValues expected; - expected.floor = std::floor(testInput.floor); - expected.isnan = std::isnan(testInput.isnan); - expected.isinf = std::isinf(testInput.isinf); - expected.pow = std::pow(testInput.powX, testInput.powY); - expected.exp = std::exp(testInput.exp); - expected.exp2 = std::exp2(testInput.exp2); - expected.log = std::log(testInput.log); - expected.log2 = std::log2(testInput.log2); - expected.absF = std::abs(testInput.absF); - expected.absI = std::abs(testInput.absI); - expected.sqrt = std::sqrt(testInput.sqrt); - expected.sin = std::sin(testInput.sin); - expected.cos = std::cos(testInput.cos); - expected.acos = std::acos(testInput.acos); - expected.tan = std::tan(testInput.tan); - expected.asin = std::asin(testInput.asin); - expected.atan = std::atan(testInput.atan); - expected.sinh = std::sinh(testInput.sinh); - expected.cosh = std::cosh(testInput.cosh); - expected.tanh = std::tanh(testInput.tanh); - expected.asinh = std::asinh(testInput.asinh); - expected.acosh = std::acosh(testInput.acosh); - expected.atanh = std::atanh(testInput.atanh); - expected.atan2 = std::atan2(testInput.atan2Y, testInput.atan2X); - expected.erf = std::erf(testInput.erf); + TgmathTestValues determineExpectedResults(const TgmathIntputTestValues& testInput) override + { + // use std library functions to determine expected test values, the output of functions from tgmath.hlsl will be verified against these values + TgmathTestValues expected; + expected.floor = std::floor(testInput.floor); + expected.isnan = std::isnan(testInput.isnan); + expected.isinf = std::isinf(testInput.isinf); + expected.pow = std::pow(testInput.powX, testInput.powY); + expected.exp = std::exp(testInput.exp); + expected.exp2 = std::exp2(testInput.exp2); + expected.log = std::log(testInput.log); + expected.log2 = std::log2(testInput.log2); + expected.absF = std::abs(testInput.absF); + expected.absI = std::abs(testInput.absI); + expected.sqrt = std::sqrt(testInput.sqrt); + expected.sin = std::sin(testInput.sin); + expected.cos = std::cos(testInput.cos); + expected.acos = std::acos(testInput.acos); + expected.tan = std::tan(testInput.tan); + expected.asin = std::asin(testInput.asin); + expected.atan = std::atan(testInput.atan); + expected.sinh = std::sinh(testInput.sinh); + expected.cosh = std::cosh(testInput.cosh); + expected.tanh = std::tanh(testInput.tanh); + expected.asinh = std::asinh(testInput.asinh); + expected.acosh = std::acosh(testInput.acosh); + expected.atanh = std::atanh(testInput.atanh); + expected.atan2 = std::atan2(testInput.atan2Y, testInput.atan2X); + expected.erf = std::erf(testInput.erf); + { + float tmp; + expected.modf = std::modf(testInput.modf, &tmp); + } + expected.round = std::round(testInput.round); + // TODO: uncomment when C++23 + //expected.roundEven = std::roundeven(testInput.roundEven); + // TODO: remove when C++23 + auto roundeven = [](const float& val) -> float { float tmp; - expected.modf = std::modf(testInput.modf, &tmp); - } - expected.round = std::round(testInput.round); - // TODO: uncomment when C++23 - //expected.roundEven = std::roundeven(testInput.roundEven); - // TODO: remove when C++23 - auto roundeven = [](const float& val) -> float + if (std::abs(std::modf(val, &tmp)) == 0.5f) { - float tmp; - if (std::abs(std::modf(val, &tmp)) == 0.5f) - { - int32_t result = static_cast(val); - if (result % 2 != 0) - result >= 0 ? ++result : --result; - return result; - } - - return std::round(val); - }; - expected.roundEven = roundeven(testInput.roundEven); - - expected.trunc = std::trunc(testInput.trunc); - expected.ceil = std::ceil(testInput.ceil); - expected.fma = std::fma(testInput.fmaX, testInput.fmaY, testInput.fmaZ); - expected.ldexp = std::ldexp(testInput.ldexpArg, testInput.ldexpExp); - - expected.floorVec = float32_t3(std::floor(testInput.floorVec.x), std::floor(testInput.floorVec.y), std::floor(testInput.floorVec.z)); - - expected.isnanVec = float32_t3(std::isnan(testInput.isnanVec.x), std::isnan(testInput.isnanVec.y), std::isnan(testInput.isnanVec.z)); - expected.isinfVec = float32_t3(std::isinf(testInput.isinfVec.x), std::isinf(testInput.isinfVec.y), std::isinf(testInput.isinfVec.z)); - - expected.powVec.x = std::pow(testInput.powXVec.x, testInput.powYVec.x); - expected.powVec.y = std::pow(testInput.powXVec.y, testInput.powYVec.y); - expected.powVec.z = std::pow(testInput.powXVec.z, testInput.powYVec.z); - - expected.expVec = float32_t3(std::exp(testInput.expVec.x), std::exp(testInput.expVec.y), std::exp(testInput.expVec.z)); - expected.exp2Vec = float32_t3(std::exp2(testInput.exp2Vec.x), std::exp2(testInput.exp2Vec.y), std::exp2(testInput.exp2Vec.z)); - expected.logVec = float32_t3(std::log(testInput.logVec.x), std::log(testInput.logVec.y), std::log(testInput.logVec.z)); - expected.log2Vec = float32_t3(std::log2(testInput.log2Vec.x), std::log2(testInput.log2Vec.y), std::log2(testInput.log2Vec.z)); - expected.absFVec = float32_t3(std::abs(testInput.absFVec.x), std::abs(testInput.absFVec.y), std::abs(testInput.absFVec.z)); - expected.absIVec = float32_t3(std::abs(testInput.absIVec.x), std::abs(testInput.absIVec.y), std::abs(testInput.absIVec.z)); - expected.sqrtVec = float32_t3(std::sqrt(testInput.sqrtVec.x), std::sqrt(testInput.sqrtVec.y), std::sqrt(testInput.sqrtVec.z)); - expected.cosVec = float32_t3(std::cos(testInput.cosVec.x), std::cos(testInput.cosVec.y), std::cos(testInput.cosVec.z)); - expected.sinVec = float32_t3(std::sin(testInput.sinVec.x), std::sin(testInput.sinVec.y), std::sin(testInput.sinVec.z)); - expected.tanVec = float32_t3(std::tan(testInput.tanVec.x), std::tan(testInput.tanVec.y), std::tan(testInput.tanVec.z)); - expected.asinVec = float32_t3(std::asin(testInput.asinVec.x), std::asin(testInput.asinVec.y), std::asin(testInput.asinVec.z)); - expected.atanVec = float32_t3(std::atan(testInput.atanVec.x), std::atan(testInput.atanVec.y), std::atan(testInput.atanVec.z)); - expected.sinhVec = float32_t3(std::sinh(testInput.sinhVec.x), std::sinh(testInput.sinhVec.y), std::sinh(testInput.sinhVec.z)); - expected.coshVec = float32_t3(std::cosh(testInput.coshVec.x), std::cosh(testInput.coshVec.y), std::cosh(testInput.coshVec.z)); - expected.tanhVec = float32_t3(std::tanh(testInput.tanhVec.x), std::tanh(testInput.tanhVec.y), std::tanh(testInput.tanhVec.z)); - expected.asinhVec = float32_t3(std::asinh(testInput.asinhVec.x), std::asinh(testInput.asinhVec.y), std::asinh(testInput.asinhVec.z)); - expected.acoshVec = float32_t3(std::acosh(testInput.acoshVec.x), std::acosh(testInput.acoshVec.y), std::acosh(testInput.acoshVec.z)); - expected.atanhVec = float32_t3(std::atanh(testInput.atanhVec.x), std::atanh(testInput.atanhVec.y), std::atanh(testInput.atanhVec.z)); - expected.atan2Vec = float32_t3(std::atan2(testInput.atan2YVec.x, testInput.atan2XVec.x), std::atan2(testInput.atan2YVec.y, testInput.atan2XVec.y), std::atan2(testInput.atan2YVec.z, testInput.atan2XVec.z)); - expected.acosVec = float32_t3(std::acos(testInput.acosVec.x), std::acos(testInput.acosVec.y), std::acos(testInput.acosVec.z)); - expected.erfVec = float32_t3(std::erf(testInput.erfVec.x), std::erf(testInput.erfVec.y), std::erf(testInput.erfVec.z)); - { - float tmp; - expected.modfVec = float32_t3(std::modf(testInput.modfVec.x, &tmp), std::modf(testInput.modfVec.y, &tmp), std::modf(testInput.modfVec.z, &tmp)); - } - expected.roundVec = float32_t3( - std::round(testInput.roundVec.x), - std::round(testInput.roundVec.y), - std::round(testInput.roundVec.z) - ); - // TODO: uncomment when C++23 - //expected.roundEven = float32_t( - // std::roundeven(testInput.roundEvenVec.x), - // std::roundeven(testInput.roundEvenVec.y), - // std::roundeven(testInput.roundEvenVec.z) - // ); - // TODO: remove when C++23 - expected.roundEvenVec = float32_t3( - roundeven(testInput.roundEvenVec.x), - roundeven(testInput.roundEvenVec.y), - roundeven(testInput.roundEvenVec.z) - ); - - expected.truncVec = float32_t3(std::trunc(testInput.truncVec.x), std::trunc(testInput.truncVec.y), std::trunc(testInput.truncVec.z)); - expected.ceilVec = float32_t3(std::ceil(testInput.ceilVec.x), std::ceil(testInput.ceilVec.y), std::ceil(testInput.ceilVec.z)); - expected.fmaVec = float32_t3( - std::fma(testInput.fmaXVec.x, testInput.fmaYVec.x, testInput.fmaZVec.x), - std::fma(testInput.fmaXVec.y, testInput.fmaYVec.y, testInput.fmaZVec.y), - std::fma(testInput.fmaXVec.z, testInput.fmaYVec.z, testInput.fmaZVec.z) - ); - expected.ldexpVec = float32_t3( - std::ldexp(testInput.ldexpArgVec.x, testInput.ldexpExpVec.x), - std::ldexp(testInput.ldexpArgVec.y, testInput.ldexpExpVec.y), - std::ldexp(testInput.ldexpArgVec.z, testInput.ldexpExpVec.z) - ); - - { - ModfOutput expectedModfStructOutput; - expectedModfStructOutput.fractionalPart = std::modf(testInput.modfStruct, &expectedModfStructOutput.wholeNumberPart); - expected.modfStruct = expectedModfStructOutput; - - ModfOutput expectedModfStructOutputVec; - for (int i = 0; i < 3; ++i) - expectedModfStructOutputVec.fractionalPart[i] = std::modf(testInput.modfStructVec[i], &expectedModfStructOutputVec.wholeNumberPart[i]); - expected.modfStructVec = expectedModfStructOutputVec; - } - - { - FrexpOutput expectedFrexpStructOutput; - expectedFrexpStructOutput.significand = std::frexp(testInput.frexpStruct, &expectedFrexpStructOutput.exponent); - expected.frexpStruct = expectedFrexpStructOutput; - - FrexpOutput expectedFrexpStructOutputVec; - for (int i = 0; i < 3; ++i) - expectedFrexpStructOutputVec.significand[i] = std::frexp(testInput.frexpStructVec[i], &expectedFrexpStructOutputVec.exponent[i]); - expected.frexpStructVec = expectedFrexpStructOutputVec; - } - - performCpuTests(testInput, expected); - performGpuTests(testInput, expected); + int32_t result = static_cast(val); + if (result % 2 != 0) + result >= 0 ? ++result : --result; + return result; + } + + return std::round(val); + }; + expected.roundEven = roundeven(testInput.roundEven); + + expected.trunc = std::trunc(testInput.trunc); + expected.ceil = std::ceil(testInput.ceil); + expected.fma = std::fma(testInput.fmaX, testInput.fmaY, testInput.fmaZ); + expected.ldexp = std::ldexp(testInput.ldexpArg, testInput.ldexpExp); + + expected.floorVec = float32_t3(std::floor(testInput.floorVec.x), std::floor(testInput.floorVec.y), std::floor(testInput.floorVec.z)); + + expected.isnanVec = float32_t3(std::isnan(testInput.isnanVec.x), std::isnan(testInput.isnanVec.y), std::isnan(testInput.isnanVec.z)); + expected.isinfVec = float32_t3(std::isinf(testInput.isinfVec.x), std::isinf(testInput.isinfVec.y), std::isinf(testInput.isinfVec.z)); + + expected.powVec.x = std::pow(testInput.powXVec.x, testInput.powYVec.x); + expected.powVec.y = std::pow(testInput.powXVec.y, testInput.powYVec.y); + expected.powVec.z = std::pow(testInput.powXVec.z, testInput.powYVec.z); + + expected.expVec = float32_t3(std::exp(testInput.expVec.x), std::exp(testInput.expVec.y), std::exp(testInput.expVec.z)); + expected.exp2Vec = float32_t3(std::exp2(testInput.exp2Vec.x), std::exp2(testInput.exp2Vec.y), std::exp2(testInput.exp2Vec.z)); + expected.logVec = float32_t3(std::log(testInput.logVec.x), std::log(testInput.logVec.y), std::log(testInput.logVec.z)); + expected.log2Vec = float32_t3(std::log2(testInput.log2Vec.x), std::log2(testInput.log2Vec.y), std::log2(testInput.log2Vec.z)); + expected.absFVec = float32_t3(std::abs(testInput.absFVec.x), std::abs(testInput.absFVec.y), std::abs(testInput.absFVec.z)); + expected.absIVec = float32_t3(std::abs(testInput.absIVec.x), std::abs(testInput.absIVec.y), std::abs(testInput.absIVec.z)); + expected.sqrtVec = float32_t3(std::sqrt(testInput.sqrtVec.x), std::sqrt(testInput.sqrtVec.y), std::sqrt(testInput.sqrtVec.z)); + expected.cosVec = float32_t3(std::cos(testInput.cosVec.x), std::cos(testInput.cosVec.y), std::cos(testInput.cosVec.z)); + expected.sinVec = float32_t3(std::sin(testInput.sinVec.x), std::sin(testInput.sinVec.y), std::sin(testInput.sinVec.z)); + expected.tanVec = float32_t3(std::tan(testInput.tanVec.x), std::tan(testInput.tanVec.y), std::tan(testInput.tanVec.z)); + expected.asinVec = float32_t3(std::asin(testInput.asinVec.x), std::asin(testInput.asinVec.y), std::asin(testInput.asinVec.z)); + expected.atanVec = float32_t3(std::atan(testInput.atanVec.x), std::atan(testInput.atanVec.y), std::atan(testInput.atanVec.z)); + expected.sinhVec = float32_t3(std::sinh(testInput.sinhVec.x), std::sinh(testInput.sinhVec.y), std::sinh(testInput.sinhVec.z)); + expected.coshVec = float32_t3(std::cosh(testInput.coshVec.x), std::cosh(testInput.coshVec.y), std::cosh(testInput.coshVec.z)); + expected.tanhVec = float32_t3(std::tanh(testInput.tanhVec.x), std::tanh(testInput.tanhVec.y), std::tanh(testInput.tanhVec.z)); + expected.asinhVec = float32_t3(std::asinh(testInput.asinhVec.x), std::asinh(testInput.asinhVec.y), std::asinh(testInput.asinhVec.z)); + expected.acoshVec = float32_t3(std::acosh(testInput.acoshVec.x), std::acosh(testInput.acoshVec.y), std::acosh(testInput.acoshVec.z)); + expected.atanhVec = float32_t3(std::atanh(testInput.atanhVec.x), std::atanh(testInput.atanhVec.y), std::atanh(testInput.atanhVec.z)); + expected.atan2Vec = float32_t3(std::atan2(testInput.atan2YVec.x, testInput.atan2XVec.x), std::atan2(testInput.atan2YVec.y, testInput.atan2XVec.y), std::atan2(testInput.atan2YVec.z, testInput.atan2XVec.z)); + expected.acosVec = float32_t3(std::acos(testInput.acosVec.x), std::acos(testInput.acosVec.y), std::acos(testInput.acosVec.z)); + expected.erfVec = float32_t3(std::erf(testInput.erfVec.x), std::erf(testInput.erfVec.y), std::erf(testInput.erfVec.z)); + { + float tmp; + expected.modfVec = float32_t3(std::modf(testInput.modfVec.x, &tmp), std::modf(testInput.modfVec.y, &tmp), std::modf(testInput.modfVec.z, &tmp)); } - m_logger->log("tgmath.hlsl TESTS DONE.", system::ILogger::ELL_PERFORMANCE); - } + expected.roundVec = float32_t3( + std::round(testInput.roundVec.x), + std::round(testInput.roundVec.y), + std::round(testInput.roundVec.z) + ); + // TODO: uncomment when C++23 + //expected.roundEven = float32_t( + // std::roundeven(testInput.roundEvenVec.x), + // std::roundeven(testInput.roundEvenVec.y), + // std::roundeven(testInput.roundEvenVec.z) + // ); + // TODO: remove when C++23 + expected.roundEvenVec = float32_t3( + roundeven(testInput.roundEvenVec.x), + roundeven(testInput.roundEvenVec.y), + roundeven(testInput.roundEvenVec.z) + ); + + expected.truncVec = float32_t3(std::trunc(testInput.truncVec.x), std::trunc(testInput.truncVec.y), std::trunc(testInput.truncVec.z)); + expected.ceilVec = float32_t3(std::ceil(testInput.ceilVec.x), std::ceil(testInput.ceilVec.y), std::ceil(testInput.ceilVec.z)); + expected.fmaVec = float32_t3( + std::fma(testInput.fmaXVec.x, testInput.fmaYVec.x, testInput.fmaZVec.x), + std::fma(testInput.fmaXVec.y, testInput.fmaYVec.y, testInput.fmaZVec.y), + std::fma(testInput.fmaXVec.z, testInput.fmaYVec.z, testInput.fmaZVec.z) + ); + expected.ldexpVec = float32_t3( + std::ldexp(testInput.ldexpArgVec.x, testInput.ldexpExpVec.x), + std::ldexp(testInput.ldexpArgVec.y, testInput.ldexpExpVec.y), + std::ldexp(testInput.ldexpArgVec.z, testInput.ldexpExpVec.z) + ); -private: - inline static constexpr int Iterations = 100u; + { + ModfOutput expectedModfStructOutput; + expectedModfStructOutput.fractionalPart = std::modf(testInput.modfStruct, &expectedModfStructOutput.wholeNumberPart); + expected.modfStruct = expectedModfStructOutput; + + ModfOutput expectedModfStructOutputVec; + for (int i = 0; i < 3; ++i) + expectedModfStructOutputVec.fractionalPart[i] = std::modf(testInput.modfStructVec[i], &expectedModfStructOutputVec.wholeNumberPart[i]); + expected.modfStructVec = expectedModfStructOutputVec; + } - void performCpuTests(const TgmathIntputTestValues& commonTestInputValues, const TgmathTestValues& expectedTestValues) - { - TgmathTestValues cpuTestValues; - cpuTestValues.fillTestValues(commonTestInputValues); - verifyTestValues(expectedTestValues, cpuTestValues, ITester::TestType::CPU); - - } + { + FrexpOutput expectedFrexpStructOutput; + expectedFrexpStructOutput.significand = std::frexp(testInput.frexpStruct, &expectedFrexpStructOutput.exponent); + expected.frexpStruct = expectedFrexpStructOutput; + + FrexpOutput expectedFrexpStructOutputVec; + for (int i = 0; i < 3; ++i) + expectedFrexpStructOutputVec.significand[i] = std::frexp(testInput.frexpStructVec[i], &expectedFrexpStructOutputVec.exponent[i]); + expected.frexpStructVec = expectedFrexpStructOutputVec; + } - void performGpuTests(const TgmathIntputTestValues& commonTestInputValues, const TgmathTestValues& expectedTestValues) - { - TgmathTestValues gpuTestValues; - gpuTestValues = dispatch(commonTestInputValues); - verifyTestValues(expectedTestValues, gpuTestValues, ITester::TestType::GPU); + return expected; } - void verifyTestValues(const TgmathTestValues& expectedTestValues, const TgmathTestValues& testValues, ITester::TestType testType) + void verifyTestResults(const TgmathTestValues& expectedTestValues, const TgmathTestValues& testValues, const size_t testIteration, const uint32_t seed, TestType testType) override { // TODO: figure out input for functions: sinh, cosh so output isn't a crazy low number // very low numbers generate comparison errors - verifyTestValue("floor", expectedTestValues.floor, testValues.floor, testType); - verifyTestValue("isnan", expectedTestValues.isnan, testValues.isnan, testType); - verifyTestValue("isinf", expectedTestValues.isinf, testValues.isinf, testType); - verifyTestValue("pow", expectedTestValues.pow, testValues.pow, testType); - verifyTestValue("exp", expectedTestValues.exp, testValues.exp, testType); - verifyTestValue("exp2", expectedTestValues.exp2, testValues.exp2, testType); - verifyTestValue("log", expectedTestValues.log, testValues.log, testType); - verifyTestValue("log2", expectedTestValues.log2, testValues.log2, testType); - verifyTestValue("absF", expectedTestValues.absF, testValues.absF, testType); - verifyTestValue("absI", expectedTestValues.absI, testValues.absI, testType); - verifyTestValue("sqrt", expectedTestValues.sqrt, testValues.sqrt, testType); - verifyTestValue("sin", expectedTestValues.sin, testValues.sin, testType); - verifyTestValue("cos", expectedTestValues.cos, testValues.cos, testType); - verifyTestValue("acos", expectedTestValues.acos, testValues.acos, testType); - verifyTestValue("tan", expectedTestValues.tan, testValues.tan, testType); - verifyTestValue("asin", expectedTestValues.asin, testValues.asin, testType); - verifyTestValue("atan", expectedTestValues.atan, testValues.atan, testType); - //verifyTestValue("sinh", expectedTestValues.sinh, testValues.sinh, testType); - //verifyTestValue("cosh", expectedTestValues.cosh, testValues.cosh, testType); - verifyTestValue("tanh", expectedTestValues.tanh, testValues.tanh, testType); - verifyTestValue("asinh", expectedTestValues.asinh, testValues.asinh, testType); - verifyTestValue("acosh", expectedTestValues.acosh, testValues.acosh, testType); - verifyTestValue("atanh", expectedTestValues.atanh, testValues.atanh, testType); - verifyTestValue("atan2", expectedTestValues.atan2, testValues.atan2, testType); - verifyTestValue("modf", expectedTestValues.modf, testValues.modf, testType); - verifyTestValue("round", expectedTestValues.round, testValues.round, testType); - verifyTestValue("roundEven", expectedTestValues.roundEven, testValues.roundEven, testType); - verifyTestValue("trunc", expectedTestValues.trunc, testValues.trunc, testType); - verifyTestValue("ceil", expectedTestValues.ceil, testValues.ceil, testType); - verifyTestValue("fma", expectedTestValues.fma, testValues.fma, testType); - verifyTestValue("ldexp", expectedTestValues.ldexp, testValues.ldexp, testType); - verifyTestValue("erf", expectedTestValues.erf, testValues.erf, testType); - //verifyTestValue("erfInv", expectedTestValues.erfInv, testValues.erfInv, testType); - - verifyTestVector3dValue("floorVec", expectedTestValues.floorVec, testValues.floorVec, testType); - verifyTestVector3dValue("isnanVec", expectedTestValues.isnanVec, testValues.isnanVec, testType); - verifyTestVector3dValue("isinfVec", expectedTestValues.isinfVec, testValues.isinfVec, testType); - verifyTestVector3dValue("powVec", expectedTestValues.powVec, testValues.powVec, testType); - verifyTestVector3dValue("expVec", expectedTestValues.expVec, testValues.expVec, testType); - verifyTestVector3dValue("exp2Vec", expectedTestValues.exp2Vec, testValues.exp2Vec, testType); - verifyTestVector3dValue("logVec", expectedTestValues.logVec, testValues.logVec, testType); - verifyTestVector3dValue("log2Vec", expectedTestValues.log2Vec, testValues.log2Vec, testType); - verifyTestVector3dValue("absFVec", expectedTestValues.absFVec, testValues.absFVec, testType); - verifyTestVector3dValue("absIVec", expectedTestValues.absIVec, testValues.absIVec, testType); - verifyTestVector3dValue("sqrtVec", expectedTestValues.sqrtVec, testValues.sqrtVec, testType); - verifyTestVector3dValue("sinVec", expectedTestValues.sinVec, testValues.sinVec, testType); - verifyTestVector3dValue("cosVec", expectedTestValues.cosVec, testValues.cosVec, testType); - verifyTestVector3dValue("acosVec", expectedTestValues.acosVec, testValues.acosVec, testType); - verifyTestVector3dValue("modfVec", expectedTestValues.modfVec, testValues.modfVec, testType); - verifyTestVector3dValue("roundVec", expectedTestValues.roundVec, testValues.roundVec, testType); - verifyTestVector3dValue("roundEvenVec", expectedTestValues.roundEvenVec, testValues.roundEvenVec, testType); - verifyTestVector3dValue("truncVec", expectedTestValues.truncVec, testValues.truncVec, testType); - verifyTestVector3dValue("ceilVec", expectedTestValues.ceilVec, testValues.ceilVec, testType); - verifyTestVector3dValue("fmaVec", expectedTestValues.fmaVec, testValues.fmaVec, testType); - verifyTestVector3dValue("ldexp", expectedTestValues.ldexpVec, testValues.ldexpVec, testType); - verifyTestVector3dValue("tanVec", expectedTestValues.tanVec, testValues.tanVec, testType); - verifyTestVector3dValue("asinVec", expectedTestValues.asinVec, testValues.asinVec, testType); - verifyTestVector3dValue("atanVec", expectedTestValues.atanVec, testValues.atanVec, testType); - //verifyTestVector3dValue("sinhVec", expectedTestValues.sinhVec, testValues.sinhVec, testType); - //verifyTestVector3dValue("coshVec", expectedTestValues.coshVec, testValues.coshVec, testType); - verifyTestVector3dValue("tanhVec", expectedTestValues.tanhVec, testValues.tanhVec, testType); - verifyTestVector3dValue("asinhVec", expectedTestValues.asinhVec, testValues.asinhVec, testType); - verifyTestVector3dValue("acoshVec", expectedTestValues.acoshVec, testValues.acoshVec, testType); - verifyTestVector3dValue("atanhVec", expectedTestValues.atanhVec, testValues.atanhVec, testType); - verifyTestVector3dValue("atan2Vec", expectedTestValues.atan2Vec, testValues.atan2Vec, testType); - verifyTestVector3dValue("erfVec", expectedTestValues.erfVec, testValues.erfVec, testType); - //verifyTestVector3dValue("erfInvVec", expectedTestValues.erfInvVec, testValues.erfInvVec, testType); + verifyTestValue("floor", expectedTestValues.floor, testValues.floor, testIteration, seed, testType); + verifyTestValue("isnan", expectedTestValues.isnan, testValues.isnan, testIteration, seed, testType); + verifyTestValue("isinf", expectedTestValues.isinf, testValues.isinf, testIteration, seed, testType); + verifyTestValue("pow", expectedTestValues.pow, testValues.pow, testIteration, seed, testType, 0.0001); + verifyTestValue("exp", expectedTestValues.exp, testValues.exp, testIteration, seed, testType); + verifyTestValue("exp2", expectedTestValues.exp2, testValues.exp2, testIteration, seed, testType); + verifyTestValue("log", expectedTestValues.log, testValues.log, testIteration, seed, testType); + verifyTestValue("log2", expectedTestValues.log2, testValues.log2, testIteration, seed, testType); + verifyTestValue("absF", expectedTestValues.absF, testValues.absF, testIteration, seed, testType); + verifyTestValue("absI", expectedTestValues.absI, testValues.absI, testIteration, seed, testType); + verifyTestValue("sqrt", expectedTestValues.sqrt, testValues.sqrt, testIteration, seed, testType); + verifyTestValue("sin", expectedTestValues.sin, testValues.sin, testIteration, seed, testType); + verifyTestValue("cos", expectedTestValues.cos, testValues.cos, testIteration, seed, testType); + verifyTestValue("acos", expectedTestValues.acos, testValues.acos, testIteration, seed, testType); + verifyTestValue("tan", expectedTestValues.tan, testValues.tan, testIteration, seed, testType); + verifyTestValue("asin", expectedTestValues.asin, testValues.asin, testIteration, seed, testType); + verifyTestValue("atan", expectedTestValues.atan, testValues.atan, testIteration, seed, testType); + //verifyTestValue("sinh", expectedTestValues.sinh, testValues.sinh, testIteration, seed, testType); + //verifyTestValue("cosh", expectedTestValues.cosh, testValues.cosh, testIteration, seed, testType); + verifyTestValue("tanh", expectedTestValues.tanh, testValues.tanh, testIteration, seed, testType); + verifyTestValue("asinh", expectedTestValues.asinh, testValues.asinh, testIteration, seed, testType); + verifyTestValue("acosh", expectedTestValues.acosh, testValues.acosh, testIteration, seed, testType); + verifyTestValue("atanh", expectedTestValues.atanh, testValues.atanh, testIteration, seed, testType); + verifyTestValue("atan2", expectedTestValues.atan2, testValues.atan2, testIteration, seed, testType); + verifyTestValue("modf", expectedTestValues.modf, testValues.modf, testIteration, seed, testType); + verifyTestValue("round", expectedTestValues.round, testValues.round, testIteration, seed, testType); + verifyTestValue("roundEven", expectedTestValues.roundEven, testValues.roundEven, testIteration, seed, testType); + verifyTestValue("trunc", expectedTestValues.trunc, testValues.trunc, testIteration, seed, testType); + verifyTestValue("ceil", expectedTestValues.ceil, testValues.ceil, testIteration, seed, testType); + verifyTestValue("fma", expectedTestValues.fma, testValues.fma, testIteration, seed, testType); + verifyTestValue("ldexp", expectedTestValues.ldexp, testValues.ldexp, testIteration, seed, testType); + verifyTestValue("erf", expectedTestValues.erf, testValues.erf, testIteration, seed, testType); + //verifyTestValue("erfInv", expectedTestValues.erfInv, testValues.erfInv, testIteration, seed, testType); + + verifyTestValue("floorVec", expectedTestValues.floorVec, testValues.floorVec, testIteration, seed, testType); + verifyTestValue("isnanVec", expectedTestValues.isnanVec, testValues.isnanVec, testIteration, seed, testType); + verifyTestValue("isinfVec", expectedTestValues.isinfVec, testValues.isinfVec, testIteration, seed, testType); + verifyTestValue("powVec", expectedTestValues.powVec, testValues.powVec, testIteration, seed, testType, 0.0001); + verifyTestValue("expVec", expectedTestValues.expVec, testValues.expVec, testIteration, seed, testType); + verifyTestValue("exp2Vec", expectedTestValues.exp2Vec, testValues.exp2Vec, testIteration, seed, testType); + verifyTestValue("logVec", expectedTestValues.logVec, testValues.logVec, testIteration, seed, testType); + verifyTestValue("log2Vec", expectedTestValues.log2Vec, testValues.log2Vec, testIteration, seed, testType); + verifyTestValue("absFVec", expectedTestValues.absFVec, testValues.absFVec, testIteration, seed, testType); + verifyTestValue("absIVec", expectedTestValues.absIVec, testValues.absIVec, testIteration, seed, testType); + verifyTestValue("sqrtVec", expectedTestValues.sqrtVec, testValues.sqrtVec, testIteration, seed, testType); + verifyTestValue("sinVec", expectedTestValues.sinVec, testValues.sinVec, testIteration, seed, testType); + verifyTestValue("cosVec", expectedTestValues.cosVec, testValues.cosVec, testIteration, seed, testType); + verifyTestValue("acosVec", expectedTestValues.acosVec, testValues.acosVec, testIteration, seed, testType); + verifyTestValue("modfVec", expectedTestValues.modfVec, testValues.modfVec, testIteration, seed, testType); + verifyTestValue("roundVec", expectedTestValues.roundVec, testValues.roundVec, testIteration, seed, testType); + verifyTestValue("roundEvenVec", expectedTestValues.roundEvenVec, testValues.roundEvenVec, testIteration, seed, testType); + verifyTestValue("truncVec", expectedTestValues.truncVec, testValues.truncVec, testIteration, seed, testType); + verifyTestValue("ceilVec", expectedTestValues.ceilVec, testValues.ceilVec, testIteration, seed, testType); + verifyTestValue("fmaVec", expectedTestValues.fmaVec, testValues.fmaVec, testIteration, seed, testType); + verifyTestValue("ldexp", expectedTestValues.ldexpVec, testValues.ldexpVec, testIteration, seed, testType); + verifyTestValue("tanVec", expectedTestValues.tanVec, testValues.tanVec, testIteration, seed, testType); + verifyTestValue("asinVec", expectedTestValues.asinVec, testValues.asinVec, testIteration, seed, testType); + verifyTestValue("atanVec", expectedTestValues.atanVec, testValues.atanVec, testIteration, seed, testType); + //verifyTestValue("sinhVec", expectedTestValues.sinhVec, testValues.sinhVec, testIteration, seed, testType); + //verifyTestValue("coshVec", expectedTestValues.coshVec, testValues.coshVec, testIteration, seed, testType); + verifyTestValue("tanhVec", expectedTestValues.tanhVec, testValues.tanhVec, testIteration, seed, testType); + verifyTestValue("asinhVec", expectedTestValues.asinhVec, testValues.asinhVec, testIteration, seed, testType); + verifyTestValue("acoshVec", expectedTestValues.acoshVec, testValues.acoshVec, testIteration, seed, testType); + verifyTestValue("atanhVec", expectedTestValues.atanhVec, testValues.atanhVec, testIteration, seed, testType); + verifyTestValue("atan2Vec", expectedTestValues.atan2Vec, testValues.atan2Vec, testIteration, seed, testType); + verifyTestValue("erfVec", expectedTestValues.erfVec, testValues.erfVec, testIteration, seed, testType); + //verifyTestValue("erfInvVec", expectedTestValues.erfInvVec, testValues.erfInvVec, testIteration, seed, testType); // verify output of struct producing functions - verifyTestValue("modfStruct", expectedTestValues.modfStruct.fractionalPart, testValues.modfStruct.fractionalPart, testType); - verifyTestValue("modfStruct", expectedTestValues.modfStruct.wholeNumberPart, testValues.modfStruct.wholeNumberPart, testType); - verifyTestVector3dValue("modfStructVec", expectedTestValues.modfStructVec.fractionalPart, testValues.modfStructVec.fractionalPart, testType); - verifyTestVector3dValue("modfStructVec", expectedTestValues.modfStructVec.wholeNumberPart, testValues.modfStructVec.wholeNumberPart, testType); - - verifyTestValue("frexpStruct", expectedTestValues.frexpStruct.significand, testValues.frexpStruct.significand, testType); - verifyTestValue("frexpStruct", expectedTestValues.frexpStruct.exponent, testValues.frexpStruct.exponent, testType); - verifyTestVector3dValue("frexpStructVec", expectedTestValues.frexpStructVec.significand, testValues.frexpStructVec.significand, testType); - verifyTestVector3dValue("frexpStructVec", expectedTestValues.frexpStructVec.exponent, testValues.frexpStructVec.exponent, testType); + verifyTestValue("modfStruct", expectedTestValues.modfStruct.fractionalPart, testValues.modfStruct.fractionalPart, testIteration, seed, testType); + verifyTestValue("modfStruct", expectedTestValues.modfStruct.wholeNumberPart, testValues.modfStruct.wholeNumberPart, testIteration, seed, testType); + verifyTestValue("modfStructVec", expectedTestValues.modfStructVec.fractionalPart, testValues.modfStructVec.fractionalPart, testIteration, seed, testType); + verifyTestValue("modfStructVec", expectedTestValues.modfStructVec.wholeNumberPart, testValues.modfStructVec.wholeNumberPart, testIteration, seed, testType); + + verifyTestValue("frexpStruct", expectedTestValues.frexpStruct.significand, testValues.frexpStruct.significand, testIteration, seed, testType); + verifyTestValue("frexpStruct", expectedTestValues.frexpStruct.exponent, testValues.frexpStruct.exponent, testIteration, seed, testType); + verifyTestValue("frexpStructVec", expectedTestValues.frexpStructVec.significand, testValues.frexpStructVec.significand, testIteration, seed, testType); + verifyTestValue("frexpStructVec", expectedTestValues.frexpStructVec.exponent, testValues.frexpStructVec.exponent, testIteration, seed, testType); } }; diff --git a/22_CppCompat/app_resources/common.hlsl b/22_CppCompat/app_resources/common.hlsl index e2303a2fc..7fed20bbe 100644 --- a/22_CppCompat/app_resources/common.hlsl +++ b/22_CppCompat/app_resources/common.hlsl @@ -1,74 +1,74 @@ -//// Copyright (C) 2023-2024 - DevSH Graphics Programming Sp. z O.O. -//// This file is part of the "Nabla Engine". -//// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef _NBL_EXAMPLES_TESTS_22_CPP_COMPAT_COMMON_INCLUDED_ -#define _NBL_EXAMPLES_TESTS_22_CPP_COMPAT_COMMON_INCLUDED_ - -// because DXC doesn't properly support `_Static_assert` -// TODO: add a message, and move to macros.h or cpp_compat -#define STATIC_ASSERT(...) { nbl::hlsl::conditional<__VA_ARGS__, int, void>::type a = 0; } - -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#include - - -#include -#include -#include - -#include -#include - -// tgmath.hlsl and intrinsics.hlsl tests - -using namespace nbl::hlsl; -struct TgmathIntputTestValues -{ - float floor; - float isnan; - float isinf; - float powX; - float powY; - float exp; - float exp2; - float log; - float log2; - float absF; - int absI; - float sqrt; - float sin; - float cos; - float acos; - float modf; - float round; - float roundEven; - float trunc; - float ceil; - float fmaX; - float fmaY; - float fmaZ; - float ldexpArg; - int ldexpExp; - float modfStruct; - float frexpStruct; +//// Copyright (C) 2023-2024 - DevSH Graphics Programming Sp. z O.O. +//// This file is part of the "Nabla Engine". +//// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_EXAMPLES_TESTS_22_CPP_COMPAT_COMMON_INCLUDED_ +#define _NBL_EXAMPLES_TESTS_22_CPP_COMPAT_COMMON_INCLUDED_ + +// because DXC doesn't properly support `_Static_assert` +// TODO: add a message, and move to macros.h or cpp_compat +#define STATIC_ASSERT(...) { nbl::hlsl::conditional<__VA_ARGS__, int, void>::type a = 0; } + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include + + +#include +#include +#include + +#include +#include + +// tgmath.hlsl and intrinsics.hlsl tests + +using namespace nbl::hlsl; +struct TgmathIntputTestValues +{ + float floor; + float isnan; + float isinf; + float powX; + float powY; + float exp; + float exp2; + float log; + float log2; + float absF; + int absI; + float sqrt; + float sin; + float cos; + float acos; + float modf; + float round; + float roundEven; + float trunc; + float ceil; + float fmaX; + float fmaY; + float fmaZ; + float ldexpArg; + int ldexpExp; + float modfStruct; + float frexpStruct; float tan; float asin; float atan; @@ -78,38 +78,38 @@ struct TgmathIntputTestValues float asinh; float acosh; float atanh; - float atan2X; - float atan2Y; - float erf; - float erfInv; - - float32_t3 floorVec; - float32_t3 isnanVec; - float32_t3 isinfVec; - float32_t3 powXVec; - float32_t3 powYVec; - float32_t3 expVec; - float32_t3 exp2Vec; - float32_t3 logVec; - float32_t3 log2Vec; - float32_t3 absFVec; - int32_t3 absIVec; - float32_t3 sqrtVec; - float32_t3 sinVec; - float32_t3 cosVec; - float32_t3 acosVec; - float32_t3 modfVec; - float32_t3 roundVec; - float32_t3 roundEvenVec; - float32_t3 truncVec; - float32_t3 ceilVec; - float32_t3 fmaXVec; - float32_t3 fmaYVec; - float32_t3 fmaZVec; - float32_t3 ldexpArgVec; - int32_t3 ldexpExpVec; - float32_t3 modfStructVec; - float32_t3 frexpStructVec; + float atan2X; + float atan2Y; + float erf; + float erfInv; + + float32_t3 floorVec; + float32_t3 isnanVec; + float32_t3 isinfVec; + float32_t3 powXVec; + float32_t3 powYVec; + float32_t3 expVec; + float32_t3 exp2Vec; + float32_t3 logVec; + float32_t3 log2Vec; + float32_t3 absFVec; + int32_t3 absIVec; + float32_t3 sqrtVec; + float32_t3 sinVec; + float32_t3 cosVec; + float32_t3 acosVec; + float32_t3 modfVec; + float32_t3 roundVec; + float32_t3 roundEvenVec; + float32_t3 truncVec; + float32_t3 ceilVec; + float32_t3 fmaXVec; + float32_t3 fmaYVec; + float32_t3 fmaZVec; + float32_t3 ldexpArgVec; + int32_t3 ldexpExpVec; + float32_t3 modfStructVec; + float32_t3 frexpStructVec; float32_t3 tanVec; float32_t3 asinVec; float32_t3 atanVec; @@ -119,35 +119,35 @@ struct TgmathIntputTestValues float32_t3 asinhVec; float32_t3 acoshVec; float32_t3 atanhVec; - float32_t3 atan2XVec; - float32_t3 atan2YVec; - float32_t3 erfVec; - float32_t3 erfInvVec; -}; - -struct TgmathTestValues -{ - float floor; - int isnan; - int isinf; - float pow; - float exp; - float exp2; - float log; - float log2; - float absF; - int absI; - float sqrt; - float sin; - float cos; - float acos; - float modf; - float round; - float roundEven; - float trunc; - float ceil; - float fma; - float ldexp; + float32_t3 atan2XVec; + float32_t3 atan2YVec; + float32_t3 erfVec; + float32_t3 erfInvVec; +}; + +struct TgmathTestValues +{ + float floor; + int isnan; + int isinf; + float pow; + float exp; + float exp2; + float log; + float log2; + float absF; + int absI; + float sqrt; + float sin; + float cos; + float acos; + float modf; + float round; + float roundEven; + float trunc; + float ceil; + float fma; + float ldexp; float tan; float asin; float atan; @@ -157,40 +157,40 @@ struct TgmathTestValues float asinh; float acosh; float atanh; - float atan2; - float erf; - float erfInv; - - float32_t3 floorVec; - - // we can't fix this because using namespace nbl::hlsl would cause ambiguous math functions below - // and we can't add a nbl::hlsl alias for the builtin hLSL vector type because of https://github.com/microsoft/DirectXShaderCompiler/issues/7035 -#ifndef __HLSL_VERSION - nbl::hlsl::vector isnanVec; - nbl::hlsl::vector isinfVec; -#else - vector isnanVec; - vector isinfVec; -#endif - - float32_t3 powVec; - float32_t3 expVec; - float32_t3 exp2Vec; - float32_t3 logVec; - float32_t3 log2Vec; - float32_t3 absFVec; - int32_t3 absIVec; - float32_t3 sqrtVec; - float32_t3 cosVec; - float32_t3 sinVec; - float32_t3 acosVec; - float32_t3 modfVec; - float32_t3 roundVec; - float32_t3 roundEvenVec; - float32_t3 truncVec; - float32_t3 ceilVec; - float32_t3 fmaVec; - float32_t3 ldexpVec; + float atan2; + float erf; + float erfInv; + + float32_t3 floorVec; + + // we can't fix this because using namespace nbl::hlsl would cause ambiguous math functions below + // and we can't add a nbl::hlsl alias for the builtin hLSL vector type because of https://github.com/microsoft/DirectXShaderCompiler/issues/7035 +#ifndef __HLSL_VERSION + nbl::hlsl::vector isnanVec; + nbl::hlsl::vector isinfVec; +#else + vector isnanVec; + vector isinfVec; +#endif + + float32_t3 powVec; + float32_t3 expVec; + float32_t3 exp2Vec; + float32_t3 logVec; + float32_t3 log2Vec; + float32_t3 absFVec; + int32_t3 absIVec; + float32_t3 sqrtVec; + float32_t3 cosVec; + float32_t3 sinVec; + float32_t3 acosVec; + float32_t3 modfVec; + float32_t3 roundVec; + float32_t3 roundEvenVec; + float32_t3 truncVec; + float32_t3 ceilVec; + float32_t3 fmaVec; + float32_t3 ldexpVec; float32_t3 tanVec; float32_t3 asinVec; float32_t3 atanVec; @@ -200,258 +200,281 @@ struct TgmathTestValues float32_t3 asinhVec; float32_t3 acoshVec; float32_t3 atanhVec; - float32_t3 atan2Vec; - float32_t3 erfVec; - float32_t3 erfInvVec; - - ModfOutput modfStruct; - ModfOutput modfStructVec; - FrexpOutput frexpStruct; - FrexpOutput frexpStructVec; - - void fillTestValues(NBL_CONST_REF_ARG(TgmathIntputTestValues) input) - { - floor = nbl::hlsl::floor(input.floor); - isnan = nbl::hlsl::isnan(input.isnan); - isinf = nbl::hlsl::isinf(input.isinf); - pow = nbl::hlsl::pow(input.powX, input.powY); - exp = nbl::hlsl::exp(input.exp); - exp2 = nbl::hlsl::exp2(input.exp2); - log = nbl::hlsl::log(input.log); - log2 = nbl::hlsl::log2(input.log2); - absF = nbl::hlsl::abs(input.absF); - absI = nbl::hlsl::abs(input.absI); - sqrt = nbl::hlsl::sqrt(input.sqrt); - sin = nbl::hlsl::sin(input.sin); - cos = nbl::hlsl::cos(input.cos); - tan = nbl::hlsl::tan(input.tan); - asin = nbl::hlsl::asin(input.asin); - atan = nbl::hlsl::atan(input.atan); - sinh = nbl::hlsl::sinh(input.sinh); - cosh = nbl::hlsl::cosh(input.cosh); - tanh = nbl::hlsl::tanh(input.tanh); - asinh = nbl::hlsl::asinh(input.asinh); - acosh = nbl::hlsl::acosh(input.acosh); - atanh = nbl::hlsl::atanh(input.atanh); - atan2 = nbl::hlsl::atan2(input.atan2Y, input.atan2X); - erf = nbl::hlsl::erf(input.erf); - erfInv = nbl::hlsl::erfInv(input.erfInv); - acos = nbl::hlsl::acos(input.acos); - modf = nbl::hlsl::modf(input.modf); - round = nbl::hlsl::round(input.round); - roundEven = nbl::hlsl::roundEven(input.roundEven); - trunc = nbl::hlsl::trunc(input.trunc); - ceil = nbl::hlsl::ceil(input.ceil); - fma = nbl::hlsl::fma(input.fmaX, input.fmaY, input.fmaZ); - ldexp = nbl::hlsl::ldexp(input.ldexpArg, input.ldexpExp); - - floorVec = nbl::hlsl::floor(input.floorVec); - isnanVec = nbl::hlsl::isnan(input.isnanVec); - isinfVec = nbl::hlsl::isinf(input.isinfVec); - powVec = nbl::hlsl::pow(input.powXVec, input.powYVec); - expVec = nbl::hlsl::exp(input.expVec); - exp2Vec = nbl::hlsl::exp2(input.exp2Vec); - logVec = nbl::hlsl::log(input.logVec); - log2Vec = nbl::hlsl::log2(input.log2Vec); - absFVec = nbl::hlsl::abs(input.absFVec); - absIVec = nbl::hlsl::abs(input.absIVec); - sqrtVec = nbl::hlsl::sqrt(input.sqrtVec); - sinVec = nbl::hlsl::sin(input.sinVec); - cosVec = nbl::hlsl::cos(input.cosVec); - tanVec = nbl::hlsl::tan(input.tanVec); - asinVec = nbl::hlsl::asin(input.asinVec); - atanVec = nbl::hlsl::atan(input.atanVec); - sinhVec = nbl::hlsl::sinh(input.sinhVec); - coshVec = nbl::hlsl::cosh(input.coshVec); - tanhVec = nbl::hlsl::tanh(input.tanhVec); - asinhVec = nbl::hlsl::asinh(input.asinhVec); - acoshVec = nbl::hlsl::acosh(input.acoshVec); - atanhVec = nbl::hlsl::atanh(input.atanhVec); - atan2Vec = nbl::hlsl::atan2(input.atan2YVec, input.atan2XVec); - acosVec = nbl::hlsl::acos(input.acosVec); - modfVec = nbl::hlsl::modf(input.modfVec); - roundVec = nbl::hlsl::round(input.roundVec); - roundEvenVec = nbl::hlsl::roundEven(input.roundEvenVec); - truncVec = nbl::hlsl::trunc(input.truncVec); - ceilVec = nbl::hlsl::ceil(input.ceilVec); - fmaVec = nbl::hlsl::fma(input.fmaXVec, input.fmaYVec, input.fmaZVec); - ldexpVec = nbl::hlsl::ldexp(input.ldexpArgVec, input.ldexpExpVec); - erfVec = nbl::hlsl::erf(input.erfVec); - erfInvVec = nbl::hlsl::erfInv(input.erfInvVec); - - modfStruct = nbl::hlsl::modfStruct(input.modfStruct); - modfStructVec = nbl::hlsl::modfStruct(input.modfStructVec); - frexpStruct = nbl::hlsl::frexpStruct(input.frexpStruct); - frexpStructVec = nbl::hlsl::frexpStruct(input.frexpStructVec); - } -}; - -struct IntrinsicsIntputTestValues -{ - int bitCount; - float32_t3 crossLhs; - float32_t3 crossRhs; - float clampVal; - float clampMin; - float clampMax; - float32_t3 length; - float32_t3 normalize; - float32_t3 dotLhs; - float32_t3 dotRhs; - float32_t3x3 determinant; - uint32_t findMSB; - uint32_t findLSB; - float32_t3x3 inverse; - float32_t3x3 transpose; - float32_t3x3 mulLhs; - float32_t3x3 mulRhs; - float minA; - float minB; - float maxA; - float maxB; - float rsqrt; - uint32_t bitReverse; - float frac; - float mixX; - float mixY; - float mixA; - float sign; - float radians; - float degrees; - float stepEdge; - float stepX; - float smoothStepEdge0; - float smoothStepEdge1; - float smoothStepX; - - int32_t3 bitCountVec; - float32_t3 clampValVec; - float32_t3 clampMinVec; - float32_t3 clampMaxVec; - uint32_t3 findMSBVec; - uint32_t3 findLSBVec; - float32_t3 minAVec; - float32_t3 minBVec; - float32_t3 maxAVec; - float32_t3 maxBVec; - float32_t3 rsqrtVec; - uint32_t3 bitReverseVec; - float32_t3 fracVec; - float32_t3 mixXVec; - float32_t3 mixYVec; - float32_t3 mixAVec; - float32_t3 signVec; - float32_t3 radiansVec; - float32_t3 degreesVec; - float32_t3 stepEdgeVec; - float32_t3 stepXVec; - float32_t3 smoothStepEdge0Vec; - float32_t3 smoothStepEdge1Vec; - float32_t3 smoothStepXVec; - float32_t3 faceForwardN; - float32_t3 faceForwardI; - float32_t3 faceForwardNref; - float32_t3 reflectI; - float32_t3 reflectN; - float32_t3 refractI; - float32_t3 refractN; - float refractEta; -}; - -struct IntrinsicsTestValues -{ - int bitCount; - float clamp; - float length; - float dot; - float determinant; - int findMSB; - int findLSB; - float min; - float max; - float rsqrt; - float frac; - uint32_t bitReverse; - float mix; - float sign; - float radians; - float degrees; - float step; - float smoothStep; - - float32_t3 normalize; - float32_t3 cross; - int32_t3 bitCountVec; - float32_t3 clampVec; - uint32_t3 findMSBVec; - uint32_t3 findLSBVec; - float32_t3 minVec; - float32_t3 maxVec; - float32_t3 rsqrtVec; - uint32_t3 bitReverseVec; - float32_t3 fracVec; - float32_t3 mixVec; - float32_t3 signVec; - float32_t3 radiansVec; - float32_t3 degreesVec; - float32_t3 stepVec; - float32_t3 smoothStepVec; - float32_t3 faceForward; - float32_t3 reflect; - float32_t3 refract; - - float32_t3x3 mul; - float32_t3x3 transpose; - float32_t3x3 inverse; - - void fillTestValues(NBL_CONST_REF_ARG(IntrinsicsIntputTestValues) input) - { - bitCount = nbl::hlsl::bitCount(input.bitCount); - cross = nbl::hlsl::cross(input.crossLhs, input.crossRhs); - clamp = nbl::hlsl::clamp(input.clampVal, input.clampMin, input.clampMax); - length = nbl::hlsl::length(input.length); - normalize = nbl::hlsl::normalize(input.normalize); - dot = nbl::hlsl::dot(input.dotLhs, input.dotRhs); - determinant = nbl::hlsl::determinant(input.determinant); - findMSB = nbl::hlsl::findMSB(input.findMSB); - findLSB = nbl::hlsl::findLSB(input.findLSB); - inverse = nbl::hlsl::inverse(input.inverse); - transpose = nbl::hlsl::transpose(input.transpose); - mul = nbl::hlsl::mul(input.mulLhs, input.mulRhs); - // TODO: fix min and max - min = nbl::hlsl::min(input.minA, input.minB); - max = nbl::hlsl::max(input.maxA, input.maxB); - rsqrt = nbl::hlsl::rsqrt(input.rsqrt); - bitReverse = nbl::hlsl::bitReverse(input.bitReverse); - frac = nbl::hlsl::fract(input.frac); - mix = nbl::hlsl::mix(input.mixX, input.mixY, input.mixA); - sign = nbl::hlsl::sign(input.sign); - radians = nbl::hlsl::radians(input.radians); - degrees = nbl::hlsl::degrees(input.degrees); - step = nbl::hlsl::step(input.stepEdge, input.stepX); - smoothStep = nbl::hlsl::smoothStep(input.smoothStepEdge0, input.smoothStepEdge1, input.smoothStepX); - - bitCountVec = nbl::hlsl::bitCount(input.bitCountVec); - clampVec = nbl::hlsl::clamp(input.clampValVec, input.clampMinVec, input.clampMaxVec); - findMSBVec = nbl::hlsl::findMSB(input.findMSBVec); - findLSBVec = nbl::hlsl::findLSB(input.findLSBVec); - // TODO: fix min and max - minVec = nbl::hlsl::min(input.minAVec, input.minBVec); - maxVec = nbl::hlsl::max(input.maxAVec, input.maxBVec); - rsqrtVec = nbl::hlsl::rsqrt(input.rsqrtVec); - bitReverseVec = nbl::hlsl::bitReverse(input.bitReverseVec); - fracVec = nbl::hlsl::fract(input.fracVec); - mixVec = nbl::hlsl::mix(input.mixXVec, input.mixYVec, input.mixAVec); - - signVec = nbl::hlsl::sign(input.signVec); - radiansVec = nbl::hlsl::radians(input.radiansVec); - degreesVec = nbl::hlsl::degrees(input.degreesVec); - stepVec = nbl::hlsl::step(input.stepEdgeVec, input.stepXVec); - smoothStepVec = nbl::hlsl::smoothStep(input.smoothStepEdge0Vec, input.smoothStepEdge1Vec, input.smoothStepXVec); - faceForward = nbl::hlsl::faceForward(input.faceForwardN, input.faceForwardI, input.faceForwardNref); - reflect = nbl::hlsl::reflect(input.reflectI, input.reflectN); - refract = nbl::hlsl::refract(input.refractI, input.refractN, input.refractEta); - } -}; - -#endif + float32_t3 atan2Vec; + float32_t3 erfVec; + float32_t3 erfInvVec; + + ModfOutput modfStruct; + ModfOutput modfStructVec; + FrexpOutput frexpStruct; + FrexpOutput frexpStructVec; +}; + +struct IntrinsicsIntputTestValues +{ + int bitCount; + float32_t3 crossLhs; + float32_t3 crossRhs; + float clampVal; + float clampMin; + float clampMax; + float32_t3 length; + float32_t3 normalize; + float32_t3 dotLhs; + float32_t3 dotRhs; + float32_t3x3 determinant; + uint32_t findMSB; + uint32_t findLSB; + float32_t3x3 inverse; + float32_t3x3 transpose; + float32_t3x3 mulLhs; + float32_t3x3 mulRhs; + float minA; + float minB; + float maxA; + float maxB; + float rsqrt; + uint32_t bitReverse; + float frac; + float mixX; + float mixY; + float mixA; + float sign; + float radians; + float degrees; + float stepEdge; + float stepX; + float smoothStepEdge0; + float smoothStepEdge1; + float smoothStepX; + uint32_t addCarryA; + uint32_t addCarryB; + uint32_t subBorrowA; + uint32_t subBorrowB; + + int32_t3 bitCountVec; + float32_t3 clampValVec; + float32_t3 clampMinVec; + float32_t3 clampMaxVec; + uint32_t3 findMSBVec; + uint32_t3 findLSBVec; + float32_t3 minAVec; + float32_t3 minBVec; + float32_t3 maxAVec; + float32_t3 maxBVec; + float32_t3 rsqrtVec; + uint32_t3 bitReverseVec; + float32_t3 fracVec; + float32_t3 mixXVec; + float32_t3 mixYVec; + float32_t3 mixAVec; + float32_t3 signVec; + float32_t3 radiansVec; + float32_t3 degreesVec; + float32_t3 stepEdgeVec; + float32_t3 stepXVec; + float32_t3 smoothStepEdge0Vec; + float32_t3 smoothStepEdge1Vec; + float32_t3 smoothStepXVec; + float32_t3 faceForwardN; + float32_t3 faceForwardI; + float32_t3 faceForwardNref; + float32_t3 reflectI; + float32_t3 reflectN; + float32_t3 refractI; + float32_t3 refractN; + float refractEta; + uint32_t3 addCarryAVec; + uint32_t3 addCarryBVec; + uint32_t3 subBorrowAVec; + uint32_t3 subBorrowBVec; +}; + +struct IntrinsicsTestValues +{ + int bitCount; + float clamp; + float length; + float dot; + float determinant; + int findMSB; + int findLSB; + float min; + float max; + float rsqrt; + float frac; + uint32_t bitReverse; + float mix; + float sign; + float radians; + float degrees; + float step; + float smoothStep; + + float32_t3 normalize; + float32_t3 cross; + int32_t3 bitCountVec; + float32_t3 clampVec; + uint32_t3 findMSBVec; + uint32_t3 findLSBVec; + float32_t3 minVec; + float32_t3 maxVec; + float32_t3 rsqrtVec; + uint32_t3 bitReverseVec; + float32_t3 fracVec; + float32_t3 mixVec; + float32_t3 signVec; + float32_t3 radiansVec; + float32_t3 degreesVec; + float32_t3 stepVec; + float32_t3 smoothStepVec; + float32_t3 faceForward; + float32_t3 reflect; + float32_t3 refract; + + float32_t3x3 mul; + float32_t3x3 transpose; + float32_t3x3 inverse; + + spirv::AddCarryOutput addCarry; + spirv::SubBorrowOutput subBorrow; + spirv::AddCarryOutput addCarryVec; + spirv::SubBorrowOutput subBorrowVec; +}; + +struct IntrinsicsTestExecutor +{ + void operator()(NBL_CONST_REF_ARG(IntrinsicsIntputTestValues) input, NBL_REF_ARG(IntrinsicsTestValues) output) + { + output.bitCount = nbl::hlsl::bitCount(input.bitCount); + output.cross = nbl::hlsl::cross(input.crossLhs, input.crossRhs); + output.clamp = nbl::hlsl::clamp(input.clampVal, input.clampMin, input.clampMax); + output.length = nbl::hlsl::length(input.length); + output.normalize = nbl::hlsl::normalize(input.normalize); + output.dot = nbl::hlsl::dot(input.dotLhs, input.dotRhs); + output.determinant = nbl::hlsl::determinant(input.determinant); + output.findMSB = nbl::hlsl::findMSB(input.findMSB); + output.findLSB = nbl::hlsl::findLSB(input.findLSB); + output.inverse = nbl::hlsl::inverse(input.inverse); + output.transpose = nbl::hlsl::transpose(input.transpose); + output.mul = nbl::hlsl::mul(input.mulLhs, input.mulRhs); + // TODO: fix min and max + output.min = nbl::hlsl::min(input.minA, input.minB); + output.max = nbl::hlsl::max(input.maxA, input.maxB); + output.rsqrt = nbl::hlsl::rsqrt(input.rsqrt); + output.bitReverse = nbl::hlsl::bitReverse(input.bitReverse); + output.frac = nbl::hlsl::fract(input.frac); + output.mix = nbl::hlsl::mix(input.mixX, input.mixY, input.mixA); + output.sign = nbl::hlsl::sign(input.sign); + output.radians = nbl::hlsl::radians(input.radians); + output.degrees = nbl::hlsl::degrees(input.degrees); + output.step = nbl::hlsl::step(input.stepEdge, input.stepX); + output.smoothStep = nbl::hlsl::smoothStep(input.smoothStepEdge0, input.smoothStepEdge1, input.smoothStepX); + + output.bitCountVec = nbl::hlsl::bitCount(input.bitCountVec); + output.clampVec = nbl::hlsl::clamp(input.clampValVec, input.clampMinVec, input.clampMaxVec); + output.findMSBVec = nbl::hlsl::findMSB(input.findMSBVec); + output.findLSBVec = nbl::hlsl::findLSB(input.findLSBVec); + // TODO: fix min and max + output.minVec = nbl::hlsl::min(input.minAVec, input.minBVec); + output.maxVec = nbl::hlsl::max(input.maxAVec, input.maxBVec); + output.rsqrtVec = nbl::hlsl::rsqrt(input.rsqrtVec); + output.bitReverseVec = nbl::hlsl::bitReverse(input.bitReverseVec); + output.fracVec = nbl::hlsl::fract(input.fracVec); + output.mixVec = nbl::hlsl::mix(input.mixXVec, input.mixYVec, input.mixAVec); + + output.signVec = nbl::hlsl::sign(input.signVec); + output.radiansVec = nbl::hlsl::radians(input.radiansVec); + output.degreesVec = nbl::hlsl::degrees(input.degreesVec); + output.stepVec = nbl::hlsl::step(input.stepEdgeVec, input.stepXVec); + output.smoothStepVec = nbl::hlsl::smoothStep(input.smoothStepEdge0Vec, input.smoothStepEdge1Vec, input.smoothStepXVec); + output.faceForward = nbl::hlsl::faceForward(input.faceForwardN, input.faceForwardI, input.faceForwardNref); + output.reflect = nbl::hlsl::reflect(input.reflectI, input.reflectN); + output.refract = nbl::hlsl::refract(input.refractI, input.refractN, input.refractEta); + output.addCarry = nbl::hlsl::addCarry(input.addCarryA, input.addCarryB); + output.subBorrow = nbl::hlsl::subBorrow(input.subBorrowA, input.subBorrowB); + output.addCarryVec = nbl::hlsl::addCarry(input.addCarryAVec, input.addCarryBVec); + output.subBorrowVec = nbl::hlsl::subBorrow(input.subBorrowAVec, input.subBorrowBVec); + } +}; + +struct TgmathTestExecutor +{ + void operator()(NBL_CONST_REF_ARG(TgmathIntputTestValues) input, NBL_REF_ARG(TgmathTestValues) output) + { + output.floor = nbl::hlsl::floor(input.floor); + output.isnan = nbl::hlsl::isnan(input.isnan); + output.isinf = nbl::hlsl::isinf(input.isinf); + output.pow = nbl::hlsl::pow(input.powX, input.powY); + output.exp = nbl::hlsl::exp(input.exp); + output.exp2 = nbl::hlsl::exp2(input.exp2); + output.log = nbl::hlsl::log(input.log); + output.log2 = nbl::hlsl::log2(input.log2); + output.absF = nbl::hlsl::abs(input.absF); + output.absI = nbl::hlsl::abs(input.absI); + output.sqrt = nbl::hlsl::sqrt(input.sqrt); + output.sin = nbl::hlsl::sin(input.sin); + output.cos = nbl::hlsl::cos(input.cos); + output.tan = nbl::hlsl::tan(input.tan); + output.asin = nbl::hlsl::asin(input.asin); + output.atan = nbl::hlsl::atan(input.atan); + output.sinh = nbl::hlsl::sinh(input.sinh); + output.cosh = nbl::hlsl::cosh(input.cosh); + output.tanh = nbl::hlsl::tanh(input.tanh); + output.asinh = nbl::hlsl::asinh(input.asinh); + output.acosh = nbl::hlsl::acosh(input.acosh); + output.atanh = nbl::hlsl::atanh(input.atanh); + output.atan2 = nbl::hlsl::atan2(input.atan2Y, input.atan2X); + output.erf = nbl::hlsl::erf(input.erf); + output.erfInv = nbl::hlsl::erfInv(input.erfInv); + output.acos = nbl::hlsl::acos(input.acos); + output.modf = nbl::hlsl::modf(input.modf); + output.round = nbl::hlsl::round(input.round); + output.roundEven = nbl::hlsl::roundEven(input.roundEven); + output.trunc = nbl::hlsl::trunc(input.trunc); + output.ceil = nbl::hlsl::ceil(input.ceil); + output.fma = nbl::hlsl::fma(input.fmaX, input.fmaY, input.fmaZ); + output.ldexp = nbl::hlsl::ldexp(input.ldexpArg, input.ldexpExp); + + output.floorVec = nbl::hlsl::floor(input.floorVec); + output.isnanVec = nbl::hlsl::isnan(input.isnanVec); + output.isinfVec = nbl::hlsl::isinf(input.isinfVec); + output.powVec = nbl::hlsl::pow(input.powXVec, input.powYVec); + output.expVec = nbl::hlsl::exp(input.expVec); + output.exp2Vec = nbl::hlsl::exp2(input.exp2Vec); + output.logVec = nbl::hlsl::log(input.logVec); + output.log2Vec = nbl::hlsl::log2(input.log2Vec); + output.absFVec = nbl::hlsl::abs(input.absFVec); + output.absIVec = nbl::hlsl::abs(input.absIVec); + output.sqrtVec = nbl::hlsl::sqrt(input.sqrtVec); + output.sinVec = nbl::hlsl::sin(input.sinVec); + output.cosVec = nbl::hlsl::cos(input.cosVec); + output.tanVec = nbl::hlsl::tan(input.tanVec); + output.asinVec = nbl::hlsl::asin(input.asinVec); + output.atanVec = nbl::hlsl::atan(input.atanVec); + output.sinhVec = nbl::hlsl::sinh(input.sinhVec); + output.coshVec = nbl::hlsl::cosh(input.coshVec); + output.tanhVec = nbl::hlsl::tanh(input.tanhVec); + output.asinhVec = nbl::hlsl::asinh(input.asinhVec); + output.acoshVec = nbl::hlsl::acosh(input.acoshVec); + output.atanhVec = nbl::hlsl::atanh(input.atanhVec); + output.atan2Vec = nbl::hlsl::atan2(input.atan2YVec, input.atan2XVec); + output.acosVec = nbl::hlsl::acos(input.acosVec); + output.modfVec = nbl::hlsl::modf(input.modfVec); + output.roundVec = nbl::hlsl::round(input.roundVec); + output.roundEvenVec = nbl::hlsl::roundEven(input.roundEvenVec); + output.truncVec = nbl::hlsl::trunc(input.truncVec); + output.ceilVec = nbl::hlsl::ceil(input.ceilVec); + output.fmaVec = nbl::hlsl::fma(input.fmaXVec, input.fmaYVec, input.fmaZVec); + output.ldexpVec = nbl::hlsl::ldexp(input.ldexpArgVec, input.ldexpExpVec); + output.erfVec = nbl::hlsl::erf(input.erfVec); + output.erfInvVec = nbl::hlsl::erfInv(input.erfInvVec); + + output.modfStruct = nbl::hlsl::modfStruct(input.modfStruct); + output.modfStructVec = nbl::hlsl::modfStruct(input.modfStructVec); + output.frexpStruct = nbl::hlsl::frexpStruct(input.frexpStruct); + output.frexpStructVec = nbl::hlsl::frexpStruct(input.frexpStructVec); + } +}; + +#endif diff --git a/22_CppCompat/app_resources/intrinsicsTest.comp.hlsl b/22_CppCompat/app_resources/intrinsicsTest.comp.hlsl index df7cef1cf..23579cd09 100644 --- a/22_CppCompat/app_resources/intrinsicsTest.comp.hlsl +++ b/22_CppCompat/app_resources/intrinsicsTest.comp.hlsl @@ -4,13 +4,16 @@ #pragma shader_stage(compute) #include "common.hlsl" +#include [[vk::binding(0, 0)]] RWStructuredBuffer inputTestValues; [[vk::binding(1, 0)]] RWStructuredBuffer outputTestValues; [numthreads(256, 1, 1)] -void main(uint3 invocationID : SV_DispatchThreadID) +[shader("compute")] +void main() { - if(invocationID.x == 0) - outputTestValues[0].fillTestValues(inputTestValues[0]); -} + const uint invID = nbl::hlsl::glsl::gl_GlobalInvocationID().x; + IntrinsicsTestExecutor executor; + executor(inputTestValues[invID], outputTestValues[invID]); +} \ No newline at end of file diff --git a/22_CppCompat/app_resources/test.comp.hlsl b/22_CppCompat/app_resources/test.comp.hlsl index 98be76c53..9a817e021 100644 --- a/22_CppCompat/app_resources/test.comp.hlsl +++ b/22_CppCompat/app_resources/test.comp.hlsl @@ -1,10 +1,9 @@ //// Copyright (C) 2023-2024 - 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 "app_resources/common.hlsl" +#pragma shader_stage(compute) -template -const static bool is_same_v = nbl::hlsl::is_same_v; +#include "app_resources/common.hlsl" struct PushConstants @@ -88,6 +87,7 @@ struct device_capabilities2 }; [numthreads(8, 8, 1)] +[shader("compute")] void main(uint3 invocationID : SV_DispatchThreadID) { fill(invocationID, 1); @@ -157,9 +157,9 @@ void main(uint3 invocationID : SV_DispatchThreadID) { static const uint16_t TEST_VALUE_0 = 5; static const uint32_t TEST_VALUE_1 = 0x80000000u; - static const uint32_t TEST_VALUE_2 = 0x8000000000000000u; + static const uint32_t TEST_VALUE_2 = 0x8000000000000000u; // TODO: Przmek is this intended? it warns because its too big from uint32_t static const uint32_t TEST_VALUE_3 = 0x00000001u; - static const uint32_t TEST_VALUE_4 = 0x0000000000000001u; + static const uint32_t TEST_VALUE_4 = 0x0000000000000001u; // TODO: Przmek is this intended? it warns because its too big from uint32_t fill(invocationID, 5.01); diff --git a/22_CppCompat/app_resources/tgmathTest.comp.hlsl b/22_CppCompat/app_resources/tgmathTest.comp.hlsl index 5d93ffb64..4aeecb91d 100644 --- a/22_CppCompat/app_resources/tgmathTest.comp.hlsl +++ b/22_CppCompat/app_resources/tgmathTest.comp.hlsl @@ -4,13 +4,16 @@ #pragma shader_stage(compute) #include "common.hlsl" +#include [[vk::binding(0, 0)]] RWStructuredBuffer inputTestValues; [[vk::binding(1, 0)]] RWStructuredBuffer outputTestValues; [numthreads(256, 1, 1)] -void main(uint3 invocationID : SV_DispatchThreadID) +[shader("compute")] +void main() { - if(invocationID.x == 0) - outputTestValues[0].fillTestValues(inputTestValues[0]); -} + const uint invID = nbl::hlsl::glsl::gl_GlobalInvocationID().x; + TgmathTestExecutor executor; + executor(inputTestValues[invID], outputTestValues[invID]); +} \ No newline at end of file diff --git a/22_CppCompat/main.cpp b/22_CppCompat/main.cpp index 70c8d7b3a..6a8e51cf2 100644 --- a/22_CppCompat/main.cpp +++ b/22_CppCompat/main.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2018-2024 - 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/this_example/builtin/build/spirv/keys.hpp" #include "app_resources/common.hlsl" @@ -59,25 +59,33 @@ class CompatibilityTest final : public application_templates::MonoDeviceApplicat if (!asset_base_t::onAppInitialized(std::move(system))) return false; - ITester::PipelineSetupData pplnSetupData; - pplnSetupData.device = m_device; - pplnSetupData.api = m_api; - pplnSetupData.assetMgr = m_assetMgr; - pplnSetupData.logger = m_logger; - pplnSetupData.physicalDevice = m_physicalDevice; - pplnSetupData.computeFamilyIndex = getComputeQueue()->getFamilyIndex(); - { - CTgmathTester tgmathTester; - pplnSetupData.testShaderPath = "app_resources/tgmathTest.comp.hlsl"; - tgmathTester.setupPipeline(pplnSetupData); - tgmathTester.performTests(); + CTgmathTester::PipelineSetupData pplnSetupData; + pplnSetupData.device = m_device; + pplnSetupData.api = m_api; + pplnSetupData.assetMgr = m_assetMgr; + pplnSetupData.logger = m_logger; + pplnSetupData.physicalDevice = m_physicalDevice; + pplnSetupData.computeFamilyIndex = getComputeQueue()->getFamilyIndex(); + pplnSetupData.shaderKey = nbl::this_example::builtin::build::get_spirv_key<"tgmathTest">(m_device.get()); + + CTgmathTester tgmathTester(8); + tgmathTester.setupPipeline(pplnSetupData); + tgmathTester.performTestsAndVerifyResults("TgmathTestLog.txt"); } { - CIntrinsicsTester intrinsicsTester; - pplnSetupData.testShaderPath = "app_resources/intrinsicsTest.comp.hlsl"; - intrinsicsTester.setupPipeline(pplnSetupData); - intrinsicsTester.performTests(); + CIntrinsicsTester::PipelineSetupData pplnSetupData; + pplnSetupData.device = m_device; + pplnSetupData.api = m_api; + pplnSetupData.assetMgr = m_assetMgr; + pplnSetupData.logger = m_logger; + pplnSetupData.physicalDevice = m_physicalDevice; + pplnSetupData.computeFamilyIndex = getComputeQueue()->getFamilyIndex(); + pplnSetupData.shaderKey = nbl::this_example::builtin::build::get_spirv_key<"intrinsicsTest">(m_device.get()); + + CIntrinsicsTester intrinsicsTester(8); + intrinsicsTester.setupPipeline(pplnSetupData); + intrinsicsTester.performTestsAndVerifyResults("IntrinsicsTestLog.txt"); } m_queue = m_device->getQueue(0, 0); @@ -88,8 +96,9 @@ class CompatibilityTest final : public application_templates::MonoDeviceApplicat { IAssetLoader::SAssetLoadParams lp = {}; lp.logger = m_logger.get(); - lp.workingDirectory = ""; // virtual root - auto assetBundle = m_assetMgr->getAsset("app_resources/test.comp.hlsl", lp); + lp.workingDirectory = "app_resources"; // virtual root + auto key = nbl::this_example::builtin::build::get_spirv_key<"intrinsicsTest">(m_device.get()); + auto assetBundle = m_assetMgr->getAsset(key.data(), lp); const auto assets = assetBundle.getContents(); if (assets.empty()) return logFail("Could not load shader!"); diff --git a/24_ColorSpaceTest/CMakeLists.txt b/24_ColorSpaceTest/CMakeLists.txt index 026add505..a2feb2cb8 100644 --- a/24_ColorSpaceTest/CMakeLists.txt +++ b/24_ColorSpaceTest/CMakeLists.txt @@ -32,4 +32,49 @@ add_test(NAME NBL_IMAGE_HASH_RUN_TESTS COMMAND "$" --test hash WORKING_DIRECTORY "$" COMMAND_EXPAND_LISTS +) + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/present.frag.hlsl + app_resources/push_constants.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/present.frag.hlsl", + "KEY": "present", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} ) \ No newline at end of file diff --git a/24_ColorSpaceTest/main.cpp b/24_ColorSpaceTest/main.cpp index 84c55ef3a..15bc3b6da 100644 --- a/24_ColorSpaceTest/main.cpp +++ b/24_ColorSpaceTest/main.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2018-2024 - 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/this_example/builtin/build/spirv/keys.hpp" #include "nbl/examples/examples.hpp" #include "nbl/ext/FullScreenTriangle/FullScreenTriangle.h" @@ -160,26 +161,24 @@ class ColorSpaceTestSampleApp final : public SimpleWindowedApplication, public B return logFail("Failed to create Full Screen Triangle protopipeline or load its vertex shader!"); // Load Custom Shader - auto loadCompileAndCreateShader = [&](const std::string& relPath) -> smart_refctd_ptr - { - IAssetLoader::SAssetLoadParams lp = {}; - lp.logger = m_logger.get(); - lp.workingDirectory = ""; // virtual root - auto assetBundle = m_assetMgr->getAsset(relPath, lp); - const auto assets = assetBundle.getContents(); - if (assets.empty()) - return nullptr; - - // lets go straight from ICPUSpecializedShader to IGPUSpecializedShader - auto source = IAsset::castDown(assets[0]); - if (!source) - return nullptr; + auto loadPrecompiledShader = [&]() -> smart_refctd_ptr + { + IAssetLoader::SAssetLoadParams lp = {}; + lp.logger = m_logger.get(); + lp.workingDirectory = "app_resources"; + + auto key = nbl::this_example::builtin::build::get_spirv_key(m_device.get()); + auto assetBundle = m_assetMgr->getAsset(key.data(), lp); + const auto assets = assetBundle.getContents(); + if (assets.empty()) + return nullptr; - return m_device->compileShader({ source.get() }); - }; - auto fragmentShader = loadCompileAndCreateShader("app_resources/present.frag.hlsl"); + auto shader = IAsset::castDown(assets[0]); + return shader; + }; + auto fragmentShader = loadPrecompiledShader.operator()<"present">(); // "app_resources/present.frag.hlsl" if (!fragmentShader) - return logFail("Failed to Load and Compile Fragment Shader!"); + return logFail("Failed to load precompiled fragment shader!"); // Now surface indep resources m_semaphore = m_device->createSemaphore(m_submitIx); @@ -562,7 +561,7 @@ class ColorSpaceTestSampleApp final : public SimpleWindowedApplication, public B const std::string prettyJson = current.data.dump(4); if (options.verbose) - m_logger->log(prettyJson, ILogger::ELL_INFO); + m_logger->log("%s", ILogger::ELL_INFO, prettyJson); system::ISystem::future_t> future; m_system->createFile(future, current.path, system::IFileBase::ECF_WRITE); diff --git a/27_MPMCScheduler/app_resources/common.hlsl b/27_MPMCScheduler/app_resources/common.hlsl index 2fb8971ad..2783f13a2 100644 --- a/27_MPMCScheduler/app_resources/common.hlsl +++ b/27_MPMCScheduler/app_resources/common.hlsl @@ -1,8 +1,8 @@ #include "nbl/builtin/hlsl/cpp_compat.hlsl" -NBL_CONSTEXPR uint32_t WorkgroupSizeX = 8; -NBL_CONSTEXPR uint32_t WorkgroupSizeY = 8; -NBL_CONSTEXPR uint32_t WorkgroupSize = WorkgroupSizeX*WorkgroupSizeY; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t WorkgroupSizeX = 8; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t WorkgroupSizeY = 8; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t WorkgroupSize = WorkgroupSizeX*WorkgroupSizeY; struct PushConstants { diff --git a/28_FFTBloom/app_resources/fft_convolve_ifft.hlsl b/28_FFTBloom/app_resources/fft_convolve_ifft.hlsl index 07c2ec8cf..02ae4ff40 100644 --- a/28_FFTBloom/app_resources/fft_convolve_ifft.hlsl +++ b/28_FFTBloom/app_resources/fft_convolve_ifft.hlsl @@ -68,8 +68,6 @@ struct PreloadedSecondAxisAccessor : PreloadedAccessorMirrorTradeBase // This one shows up a lot so we give it a name const bool oddThread = glsl::gl_SubgroupInvocationID() & 1u; - ternary_operator > ternaryOp; - // Since every two consecutive columns are stored as one packed column, we divide the index by 2 to get the index of that packed column const uint32_t firstIndex = workgroup::SubgroupContiguousIndex() / 2; int32_t paddedIndex = int32_t(firstIndex) - pushConstants.halfPadding; @@ -82,7 +80,7 @@ struct PreloadedSecondAxisAccessor : PreloadedAccessorMirrorTradeBase { // If mirrored, we need to invert which thread is loading lo and which is loading hi // If using zero-padding, useful to find out if we're outside of [0,1) bounds - bool invert = paddedIndex < 0 || paddedIndex >= pushConstants.imageHalfRowLength; + bool inPadding = paddedIndex < 0 || paddedIndex >= pushConstants.imageHalfRowLength; int32_t wrappedIndex = paddedIndex < 0 ? ~paddedIndex : paddedIndex; // ~x = - x - 1 in two's complement (except maybe at the borders of representable range) wrappedIndex = paddedIndex < pushConstants.imageHalfRowLength ? wrappedIndex : pushConstants.imageRowLength + ~paddedIndex; const complex_t loOrHi = colMajorAccessor.get(colMajorOffset(wrappedIndex, y)); @@ -93,17 +91,17 @@ struct PreloadedSecondAxisAccessor : PreloadedAccessorMirrorTradeBase if (glsl::gl_WorkGroupID().x) { - complex_t lo = ternaryOp(oddThread, otherThreadLoOrHi, loOrHi); - complex_t hi = ternaryOp(oddThread, loOrHi, otherThreadLoOrHi); + complex_t lo = nbl::hlsl::select(oddThread, otherThreadLoOrHi, loOrHi); + complex_t hi = nbl::hlsl::select(oddThread, loOrHi, otherThreadLoOrHi); fft::unpack(lo, hi); // --------------------------------------------------- MIRROR PADDING ------------------------------------------------------------------------------------------- #ifdef MIRROR_PADDING - preloaded[localElementIndex] = ternaryOp(oddThread ^ invert, hi, lo); + preloaded[localElementIndex] = nbl::hlsl::select(oddThread != inPadding, hi, lo); // ----------------------------------------------------- ZERO PADDING ------------------------------------------------------------------------------------------- #else const complex_t Zero = { scalar_t(0), scalar_t(0) }; - preloaded[localElementIndex] = ternaryOp(invert, Zero, ternaryOp(oddThread, hi, lo)); + preloaded[localElementIndex] = nbl::hlsl::select(inPadding, Zero, nbl::hlsl::select(oddThread, hi, lo)); #endif // ------------------------------------------------ END PADDING DIVERGENCE ---------------------------------------------------------------------------------------- } @@ -116,7 +114,7 @@ struct PreloadedSecondAxisAccessor : PreloadedAccessorMirrorTradeBase const complex_t evenThreadLo = { loOrHi.real(), otherThreadLoOrHi.real() }; // Odd thread writes `hi = Z1 + iN1` const complex_t oddThreadHi = { otherThreadLoOrHi.imag(), loOrHi.imag() }; - preloaded[localElementIndex] = ternaryOp(oddThread ^ invert, oddThreadHi, evenThreadLo); + preloaded[localElementIndex] = nbl::hlsl::select(oddThread != inPadding, oddThreadHi, evenThreadLo); } paddedIndex += WorkgroupSize / 2; } diff --git a/28_FFTBloom/app_resources/kernel_fft_second_axis.hlsl b/28_FFTBloom/app_resources/kernel_fft_second_axis.hlsl index eaecb5d0f..eca81e859 100644 --- a/28_FFTBloom/app_resources/kernel_fft_second_axis.hlsl +++ b/28_FFTBloom/app_resources/kernel_fft_second_axis.hlsl @@ -46,8 +46,6 @@ struct PreloadedSecondAxisAccessor : MultiChannelPreloadedAccessorMirrorTradeBas // This one shows up a lot so we give it a name const bool oddThread = glsl::gl_SubgroupInvocationID() & 1u; - ternary_operator > ternaryOp; - if (glsl::gl_WorkGroupID().x) { // Even thread must index a y corresponding to an even element of the previous FFT pass, and the odd thread must index its DFT Mirror @@ -72,10 +70,10 @@ struct PreloadedSecondAxisAccessor : MultiChannelPreloadedAccessorMirrorTradeBas const vector loOrHiVector = vector (loOrHi.real(), loOrHi.imag()); const vector otherThreadloOrHiVector = glsl::subgroupShuffleXor< vector >(loOrHiVector, 1u); const complex_t otherThreadLoOrHi = { otherThreadloOrHiVector.x, otherThreadloOrHiVector.y }; - complex_t lo = ternaryOp(oddThread, otherThreadLoOrHi, loOrHi); - complex_t hi = ternaryOp(oddThread, loOrHi, otherThreadLoOrHi); + complex_t lo = nbl::hlsl::select(oddThread, otherThreadLoOrHi, loOrHi); + complex_t hi = nbl::hlsl::select(oddThread, loOrHi, otherThreadLoOrHi); fft::unpack(lo, hi); - preloaded[channel][localElementIndex] = ternaryOp(oddThread, hi, lo); + preloaded[channel][localElementIndex] = nbl::hlsl::select(oddThread, hi, lo); packedColumnIndex += WorkgroupSize / 2; } @@ -112,7 +110,7 @@ struct PreloadedSecondAxisAccessor : MultiChannelPreloadedAccessorMirrorTradeBas const complex_t evenThreadLo = { loOrHi.real(), otherThreadLoOrHi.real() }; // Odd thread writes `hi = Z1 + iN1` const complex_t oddThreadHi = { otherThreadLoOrHi.imag(), loOrHi.imag() }; - preloaded[channel][localElementIndex] = ternaryOp(oddThread, oddThreadHi, evenThreadLo); + preloaded[channel][localElementIndex] = nbl::hlsl::select(oddThread, oddThreadHi, evenThreadLo); packedColumnIndex += WorkgroupSize / 2; } diff --git a/62_CAD/CMakeLists.txt b/62_CAD/CMakeLists.txt index c3a0fa47e..0928d3b61 100644 --- a/62_CAD/CMakeLists.txt +++ b/62_CAD/CMakeLists.txt @@ -61,4 +61,72 @@ else() foreach(NBL_TARGET IN LISTS NBL_MSDFGEN_TARGETS) target_include_directories(${EXECUTABLE_NAME} PUBLIC $) endforeach() -endif() \ No newline at end of file +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + shaders/globals.hlsl + shaders/runtimeDeviceConfigCaps.hlsl + shaders/main_pipeline/common.hlsl + shaders/main_pipeline/dtm.hlsl + shaders/main_pipeline/fragment.hlsl + shaders/main_pipeline/fragment_shader.hlsl + shaders/main_pipeline/fragment_shader_debug.hlsl + shaders/main_pipeline/line_style.hlsl + shaders/main_pipeline/resolve_alphas.hlsl + shaders/main_pipeline/vertex_shader.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(REQUIRED_CAPS [=[ +{ + "kind": "features", + "name": "fragmentShaderPixelInterlock", + "type": "bool", + "values": [1] +} +]=]) + +set(JSON [=[ +[ + { + "INPUT": "shaders/main_pipeline/vertex_shader.hlsl", + "KEY": "main_pipeline_vertex_shader", + "CAPS": [${REQUIRED_CAPS}] + }, + { + "INPUT": "shaders/main_pipeline/fragment.hlsl", + "KEY": "main_pipeline_fragment_shader", + "CAPS": [${REQUIRED_CAPS}] + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) \ No newline at end of file diff --git a/62_CAD/main.cpp b/62_CAD/main.cpp index f4a886791..905177f6b 100644 --- a/62_CAD/main.cpp +++ b/62_CAD/main.cpp @@ -1,5 +1,5 @@ // TODO: Copyright notice - +#include "nbl/this_example/builtin/build/spirv/keys.hpp" #include "nbl/examples/examples.hpp" @@ -929,84 +929,29 @@ class ComputerAidedDesign final : public nbl::examples::SimpleWindowedApplicatio smart_refctd_ptr mainPipelineVertexShader = {}; std::array, 2u> geoTexturePipelineShaders = {}; { - smart_refctd_ptr shaderReadCache = nullptr; - smart_refctd_ptr shaderWriteCache = core::make_smart_refctd_ptr(); - auto shaderCachePath = localOutputCWD / "main_pipeline_shader_cache.bin"; - + // Load Custom Shader + auto loadPrecompiledShader = [&]() -> smart_refctd_ptr { - core::smart_refctd_ptr shaderReadCacheFile; + IAssetLoader::SAssetLoadParams lp = {}; + lp.logger = m_logger.get(); + lp.workingDirectory = "app_resources"; + + auto key = nbl::this_example::builtin::build::get_spirv_key(m_device.get()); + auto assetBundle = m_assetMgr->getAsset(key.data(), lp); + const auto assets = assetBundle.getContents(); + if (assets.empty()) { - system::ISystem::future_t> future; - m_system->createFile(future, shaderCachePath.c_str(), system::IFile::ECF_READ); - if (future.wait()) - { - future.acquire().move_into(shaderReadCacheFile); - if (shaderReadCacheFile) - { - const size_t size = shaderReadCacheFile->getSize(); - if (size > 0ull) - { - std::vector contents(size); - system::IFile::success_t succ; - shaderReadCacheFile->read(succ, contents.data(), 0, size); - if (succ) - shaderReadCache = IShaderCompiler::CCache::deserialize(contents); - } - } - } - else - m_logger->log("Failed Openning Shader Cache File.", ILogger::ELL_ERROR); + m_logger->log("Failed to load a precompiled ahsder.", ILogger::ELL_ERROR); + return nullptr; } + - } - - // Load Custom Shader - auto loadCompileShader = [&](const std::string& relPath) -> smart_refctd_ptr - { - IAssetLoader::SAssetLoadParams lp = {}; - lp.logger = m_logger.get(); - lp.workingDirectory = ""; // virtual root - auto assetBundle = m_assetMgr->getAsset(relPath, lp); - const auto assets = assetBundle.getContents(); - if (assets.empty()) - return nullptr; - - // lets go straight from ICPUSpecializedShader to IGPUSpecializedShader - auto source = IAsset::castDown(assets[0]); - if (!source) - return nullptr; - - return m_device->compileShader( ILogicalDevice::SShaderCreationParameters { .source = source.get(), .readCache = shaderReadCache.get(), .writeCache = shaderWriteCache.get(), .stage = IShader::E_SHADER_STAGE::ESS_ALL_OR_LIBRARY }); - }; + auto shader = IAsset::castDown(assets[0]); + return shader; + }; - mainPipelineFragmentShaders = loadCompileShader("../shaders/main_pipeline/fragment.hlsl"); - mainPipelineVertexShader = loadCompileShader("../shaders/main_pipeline/vertex_shader.hlsl"); - - core::smart_refctd_ptr shaderWriteCacheFile; - { - system::ISystem::future_t> future; - m_system->deleteFile(shaderCachePath); // temp solution instead of trimming, to make sure we won't have corrupted json - m_system->createFile(future, shaderCachePath.c_str(), system::IFile::ECF_WRITE); - if (future.wait()) - { - future.acquire().move_into(shaderWriteCacheFile); - if (shaderWriteCacheFile) - { - auto serializedCache = shaderWriteCache->serialize(); - if (shaderWriteCacheFile) - { - system::IFile::success_t succ; - shaderWriteCacheFile->write(succ, serializedCache->getPointer(), 0, serializedCache->getSize()); - if (!succ) - m_logger->log("Failed Writing To Shader Cache File.", ILogger::ELL_ERROR); - } - } - else - m_logger->log("Failed Creating Shader Cache File.", ILogger::ELL_ERROR); - } - else - m_logger->log("Failed Creating Shader Cache File.", ILogger::ELL_ERROR); - } + mainPipelineFragmentShaders = loadPrecompiledShader.operator()<"main_pipeline_fragment_shader">(); // "../shaders/main_pipeline/fragment.hlsl" + mainPipelineVertexShader = loadPrecompiledShader.operator() <"main_pipeline_vertex_shader">(); // "../shaders/main_pipeline/vertex_shader.hlsl" } // Shared Blend Params between pipelines diff --git a/62_CAD/shaders/geotexture/common.hlsl b/62_CAD/shaders/geotexture/common.hlsl index 691cd3d3b..f2053e003 100644 --- a/62_CAD/shaders/geotexture/common.hlsl +++ b/62_CAD/shaders/geotexture/common.hlsl @@ -4,7 +4,7 @@ #include "../globals.hlsl" // Handle multiple geo textures, separate set, array of texture? index allocator? or multiple sets? -NBL_CONSTEXPR uint32_t MaxGeoTextures = 256; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t MaxGeoTextures = 256; // GeoTexture Oriented Bounding Box struct GeoTextureOBB diff --git a/62_CAD/shaders/globals.hlsl b/62_CAD/shaders/globals.hlsl index 5c3681910..ead5a5fd9 100644 --- a/62_CAD/shaders/globals.hlsl +++ b/62_CAD/shaders/globals.hlsl @@ -1,12 +1,6 @@ #ifndef _CAD_EXAMPLE_GLOBALS_HLSL_INCLUDED_ #define _CAD_EXAMPLE_GLOBALS_HLSL_INCLUDED_ -#ifdef __HLSL_VERSION -#ifndef NBL_USE_SPIRV_BUILTINS -#include "runtimeDeviceConfigCaps.hlsl" // defines DeviceConfigCaps, uses JIT device caps -#endif -#endif - // TODO[Erfan]: Turn off in the future, but keep enabled to test // #define NBL_FORCE_EMULATED_FLOAT_64 @@ -352,8 +346,8 @@ static_assert(offsetof(CurveBox, curveMax[0]) == 56u); static_assert(sizeof(CurveBox) == 80u); #endif -NBL_CONSTEXPR uint32_t InvalidRigidSegmentIndex = 0xffffffff; -NBL_CONSTEXPR float InvalidStyleStretchValue = nbl::hlsl::numeric_limits::infinity; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t InvalidRigidSegmentIndex = 0xffffffff; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float InvalidStyleStretchValue = nbl::hlsl::numeric_limits::infinity; // TODO[Przemek]: we will need something similar to LineStyles but related to heigh shading settings which is user customizable (like stipple patterns) and requires upper_bound to figure out the color based on height value. @@ -547,27 +541,27 @@ inline bool operator==(const DTMSettings& lhs, const DTMSettings& rhs) } #endif -NBL_CONSTEXPR uint32_t ImagesBindingArraySize = 128; -NBL_CONSTEXPR uint32_t MainObjectIdxBits = 24u; // It will be packed next to alpha in a texture -NBL_CONSTEXPR uint32_t AlphaBits = 32u - MainObjectIdxBits; -NBL_CONSTEXPR uint32_t MaxIndexableMainObjects = (1u << MainObjectIdxBits) - 1u; -NBL_CONSTEXPR uint32_t InvalidStyleIdx = nbl::hlsl::numeric_limits::max; -NBL_CONSTEXPR uint32_t InvalidDTMSettingsIdx = nbl::hlsl::numeric_limits::max; -NBL_CONSTEXPR uint32_t InvalidMainObjectIdx = MaxIndexableMainObjects; -NBL_CONSTEXPR uint32_t InvalidCustomProjectionIndex = nbl::hlsl::numeric_limits::max; -NBL_CONSTEXPR uint32_t InvalidCustomClipRectIndex = nbl::hlsl::numeric_limits::max; -NBL_CONSTEXPR uint32_t InvalidTextureIndex = nbl::hlsl::numeric_limits::max; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t ImagesBindingArraySize = 128; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t MainObjectIdxBits = 24u; // It will be packed next to alpha in a texture +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t AlphaBits = 32u - MainObjectIdxBits; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t MaxIndexableMainObjects = (1u << MainObjectIdxBits) - 1u; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t InvalidStyleIdx = nbl::hlsl::numeric_limits::max; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t InvalidDTMSettingsIdx = nbl::hlsl::numeric_limits::max; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t InvalidMainObjectIdx = MaxIndexableMainObjects; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t InvalidCustomProjectionIndex = nbl::hlsl::numeric_limits::max; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t InvalidCustomClipRectIndex = nbl::hlsl::numeric_limits::max; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t InvalidTextureIndex = nbl::hlsl::numeric_limits::max; // Hatches -NBL_CONSTEXPR MajorAxis SelectedMajorAxis = MajorAxis::MAJOR_Y; -NBL_CONSTEXPR MajorAxis SelectedMinorAxis = MajorAxis::MAJOR_X; //(MajorAxis) (1 - (uint32_t) SelectedMajorAxis); +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR MajorAxis SelectedMajorAxis = MajorAxis::MAJOR_Y; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR MajorAxis SelectedMinorAxis = MajorAxis::MAJOR_X; //(MajorAxis) (1 - (uint32_t) SelectedMajorAxis); // Text or MSDF Hatches -NBL_CONSTEXPR float MSDFPixelRange = 4.0f; -NBL_CONSTEXPR float MSDFPixelRangeHalf = MSDFPixelRange / 2.0f; -NBL_CONSTEXPR float MSDFSize = 64.0f; -NBL_CONSTEXPR uint32_t MSDFMips = 4; -NBL_CONSTEXPR float HatchFillMSDFSceenSpaceSize = 8.0; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float MSDFPixelRange = 4.0f; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float MSDFPixelRangeHalf = MSDFPixelRange / 2.0f; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float MSDFSize = 64.0f; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t MSDFMips = 4; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR float HatchFillMSDFSceenSpaceSize = 8.0; inline bool isInvalidGridDtmHeightValue(float value) { diff --git a/62_CAD/shaders/main_pipeline/vertex_shader.hlsl b/62_CAD/shaders/main_pipeline/vertex_shader.hlsl index 90394e935..df566f002 100644 --- a/62_CAD/shaders/main_pipeline/vertex_shader.hlsl +++ b/62_CAD/shaders/main_pipeline/vertex_shader.hlsl @@ -706,19 +706,19 @@ PSInput vtxMain(uint vertexID : SV_VertexID) if (corner.x == 0.0f && corner.y == 0.0f) { - dilationVector.x = ieee754::flipSign(dilationVector.x); + dilationVector.x = ieee754::flipSign(dilationVector.x, true); uvOffset.x = -uvOffset.x; uvOffset.y = -uvOffset.y; } else if (corner.x == 0.0f && corner.y == 1.0f) { - dilationVector.x = ieee754::flipSign(dilationVector.x); - dilationVector.y = ieee754::flipSign(dilationVector.y); + dilationVector.x = ieee754::flipSign(dilationVector.x, true); + dilationVector.y = ieee754::flipSign(dilationVector.y, true); uvOffset.x = -uvOffset.x; } else if (corner.x == 1.0f && corner.y == 1.0f) { - dilationVector.y = ieee754::flipSign(dilationVector.y); + dilationVector.y = ieee754::flipSign(dilationVector.y, true); } else if (corner.x == 1.0f && corner.y == 0.0f) { @@ -730,7 +730,7 @@ PSInput vtxMain(uint vertexID : SV_VertexID) pfloat64_t2 worldSpaceExtentsYAxisFlipped; worldSpaceExtentsYAxisFlipped.x = worldSpaceExtents.x; - worldSpaceExtentsYAxisFlipped.y = ieee754::flipSign(worldSpaceExtents.y); + worldSpaceExtentsYAxisFlipped.y = ieee754::flipSign(worldSpaceExtents.y, true); const pfloat64_t2 vtxPos = topLeft + worldSpaceExtentsYAxisFlipped * _static_cast(corner); const pfloat64_t2 dilatedVtxPos = vtxPos + dilationVector; diff --git a/64_EmulatedFloatTest/CMakeLists.txt b/64_EmulatedFloatTest/CMakeLists.txt index aae93590d..af46da896 100644 --- a/64_EmulatedFloatTest/CMakeLists.txt +++ b/64_EmulatedFloatTest/CMakeLists.txt @@ -27,4 +27,55 @@ if(MSVC) target_compile_options("${EXECUTABLE_NAME}" PUBLIC "/fp:strict") else() target_compile_options("${EXECUTABLE_NAME}" PUBLIC -ffloat-store -frounding-math -fsignaling-nans -ftrapping-math) -endif() \ No newline at end of file +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/common.hlsl + app_resources/test.comp.hlsl + app_resources/benchmark/benchmark.comp.hlsl + app_resources/benchmark/common.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/test.comp.hlsl", + "KEY": "test", + }, + { + "INPUT": "app_resources/benchmark/benchmark.comp.hlsl", + "KEY": "benchmark", + }, +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) \ No newline at end of file diff --git a/64_EmulatedFloatTest/app_resources/benchmark/benchmark.comp.hlsl b/64_EmulatedFloatTest/app_resources/benchmark/benchmark.comp.hlsl index b31da3737..a515f6bcb 100644 --- a/64_EmulatedFloatTest/app_resources/benchmark/benchmark.comp.hlsl +++ b/64_EmulatedFloatTest/app_resources/benchmark/benchmark.comp.hlsl @@ -66,6 +66,7 @@ uint64_t calcIntegral() } [numthreads(BENCHMARK_WORKGROUP_DIMENSION_SIZE_X, 1, 1)] +[shader("compute")] void main(uint3 invocationID : SV_DispatchThreadID) { static const uint32_t NativeToEmulatedRatio = 6; diff --git a/64_EmulatedFloatTest/app_resources/benchmark/common.hlsl b/64_EmulatedFloatTest/app_resources/benchmark/common.hlsl index 98875c42f..7f6d1dec1 100644 --- a/64_EmulatedFloatTest/app_resources/benchmark/common.hlsl +++ b/64_EmulatedFloatTest/app_resources/benchmark/common.hlsl @@ -4,10 +4,10 @@ #include -NBL_CONSTEXPR uint32_t BENCHMARK_WORKGROUP_DIMENSION_SIZE_X = 128u; -NBL_CONSTEXPR uint32_t BENCHMARK_WORKGROUP_DIMENSION_SIZE_Y = 1u; -NBL_CONSTEXPR uint32_t BENCHMARK_WORKGROUP_DIMENSION_SIZE_Z = 1u; -NBL_CONSTEXPR uint32_t BENCHMARK_WORKGROUP_COUNT = 1024u; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t BENCHMARK_WORKGROUP_DIMENSION_SIZE_X = 128u; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t BENCHMARK_WORKGROUP_DIMENSION_SIZE_Y = 1u; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t BENCHMARK_WORKGROUP_DIMENSION_SIZE_Z = 1u; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t BENCHMARK_WORKGROUP_COUNT = 1024u; enum EF64_BENCHMARK_MODE { diff --git a/64_EmulatedFloatTest/app_resources/common.hlsl b/64_EmulatedFloatTest/app_resources/common.hlsl index aea1ce94d..0e8762c5a 100644 --- a/64_EmulatedFloatTest/app_resources/common.hlsl +++ b/64_EmulatedFloatTest/app_resources/common.hlsl @@ -8,7 +8,7 @@ #include #include -NBL_CONSTEXPR uint32_t WORKGROUP_SIZE = 1; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t WORKGROUP_SIZE = 1; using namespace nbl; using namespace hlsl; diff --git a/64_EmulatedFloatTest/app_resources/test.comp.hlsl b/64_EmulatedFloatTest/app_resources/test.comp.hlsl index 7681e80a5..e95eadd49 100644 --- a/64_EmulatedFloatTest/app_resources/test.comp.hlsl +++ b/64_EmulatedFloatTest/app_resources/test.comp.hlsl @@ -12,6 +12,7 @@ PushConstants pc; [numthreads(WORKGROUP_SIZE, 1, 1)] +[shader("compute")] void main(uint3 invocationID : SV_DispatchThreadID) { const nbl::hlsl::emulated_float64_t a = nbl::hlsl::bit_cast >(pc.a); diff --git a/64_EmulatedFloatTest/main.cpp b/64_EmulatedFloatTest/main.cpp index 3fc635e87..ea8def7ba 100644 --- a/64_EmulatedFloatTest/main.cpp +++ b/64_EmulatedFloatTest/main.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2018-2024 - 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/this_example/builtin/build/spirv/keys.hpp" #include "nbl/examples/examples.hpp" @@ -42,13 +42,11 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso // since emulated_float64_t rounds to zero std::fesetround(FE_TOWARDZERO); - // Remember to call the base class initialization! if (!device_base_t::onAppInitialized(smart_refctd_ptr(system))) return false; if (!asset_base_t::onAppInitialized(std::move(system))) return false; - - // In contrast to fences, we just need one semaphore to rule all dispatches + return true; } @@ -97,10 +95,14 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso auto printOnFailure = [this](EmulatedFloatTestDevice device) { + std::string errorMsgPrefix = ""; if (device == EmulatedFloatTestDevice::CPU) - m_logger->log("CPU test fail:", ILogger::ELL_ERROR); + errorMsgPrefix = "CPU test fail:"; else - m_logger->log("GPU test fail:", ILogger::ELL_ERROR); + errorMsgPrefix = "GPU test fail:"; + + m_logger->log("%s", ILogger::ELL_ERROR, errorMsgPrefix.c_str()); + m_logFile << errorMsgPrefix << '\n'; }; auto printOnArithmeticFailure = [this](const char* valName, uint64_t expectedValue, uint64_t testValue, uint64_t a, uint64_t b) @@ -120,9 +122,10 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso ss << std::bitset<64>(expectedValue) << " - expectedValue bit pattern\n"; ss << std::bitset<64>(testValue) << " - testValue bit pattern \n"; - m_logger->log(ss.str().c_str(), ILogger::ELL_ERROR); + m_logger->log("%s", ILogger::ELL_ERROR, ss.str().c_str()); + m_logFile << ss.str() << '\n'; - std::cout << "ULP error: " << std::max(expectedValue, testValue) - std::min(expectedValue, testValue) << "\n\n"; + //std::cout << "ULP error: " << std::max(expectedValue, testValue) - std::min(expectedValue, testValue) << "\n\n"; }; @@ -133,14 +136,18 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso auto printOnComparisonFailure = [this](const char* valName, int expectedValue, int testValue, double a, double b) { - m_logger->log("for input values: A = %f B = %f", ILogger::ELL_ERROR, a, b); + std::string inputValuesStr = std::string("for input values: A = ") + std::to_string(a) + std::string(" B = ") + std::to_string(b); + + m_logger->log("%s", ILogger::ELL_ERROR, inputValuesStr.c_str()); + m_logFile << inputValuesStr << '\n'; std::stringstream ss; ss << valName << " not equal!"; ss << "\nexpected value: " << std::boolalpha << bool(expectedValue); ss << "\ntest value: " << std::boolalpha << bool(testValue); - m_logger->log(ss.str().c_str(), ILogger::ELL_ERROR); + m_logger->log("%s", ILogger::ELL_ERROR, ss.str().c_str()); + m_logFile << ss.str() << '\n'; }; if (calcULPError(expectedValues.int32CreateVal, testValues.int32CreateVal) > 1u) @@ -262,9 +269,10 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso { IAssetLoader::SAssetLoadParams lp = {}; lp.logger = base.m_logger.get(); - lp.workingDirectory = ""; // virtual root - // this time we load a shader directly from a file - auto assetBundle = base.m_assetMgr->getAsset("app_resources/test.comp.hlsl", lp); + lp.workingDirectory = "app_resources"; // virtual root + + auto key = nbl::this_example::builtin::build::get_spirv_key<"test">(base.m_device.get()); + auto assetBundle = base.m_assetMgr->getAsset(key.data(), lp); const auto assets = assetBundle.getContents(); if (assets.empty()) { @@ -274,26 +282,11 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso // It would be super weird if loading a shader from a file produced more than 1 asset assert(assets.size() == 1); - smart_refctd_ptr source = IAsset::castDown(assets[0]); - - auto* compilerSet = base.m_assetMgr->getCompilerSet(); - - nbl::asset::IShaderCompiler::SCompilerOptions options = {}; - options.stage = ESS_COMPUTE; - options.preprocessorOptions.targetSpirvVersion = base.m_device->getPhysicalDevice()->getLimits().spirvVersion; - options.spirvOptimizer = nullptr; - options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_SOURCE_BIT; - options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); - options.preprocessorOptions.logger = base.m_logger.get(); - options.preprocessorOptions.includeFinder = compilerSet->getShaderCompiler(source->getContentType())->getDefaultIncludeFinder(); - - auto spirv = compilerSet->compileToSPIRV(source.get(), options); - - shader = base.m_device->compileShader({spirv.get()}); + shader = IAsset::castDown(assets[0]); } if (!shader) - base.logFail("Failed to create a GPU Shader, seems the Driver doesn't like the SPIR-V we're feeding it!\n"); + base.logFail("Failed to load precompiled \"test\" shader!\n"); nbl::video::IGPUDescriptorSetLayout::SBinding bindings[1] = { { @@ -452,6 +445,10 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso m_logger->log("Correct GPU determinated values!", ILogger::ELL_PERFORMANCE); }; + m_logFile.open("EmulatedFloatTestLog.txt", std::ios::out | std::ios::trunc); + if (!m_logFile.is_open()) + m_logger->log("Failed to open log file!", system::ILogger::ELL_ERROR); + printTestOutput("emulatedFloat64RandomValuesTest", emulatedFloat64RandomValuesTest(submitter)); printTestOutput("emulatedFloat64RandomValuesTestContrastingExponents", emulatedFloat64RandomValuesTestContrastingExponents(submitter)); printTestOutput("emulatedFloat64NegAndPosZeroTest", emulatedFloat64NegAndPosZeroTest(submitter)); @@ -464,6 +461,8 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso printTestOutput("emulatedFloat64BNaNTest", emulatedFloat64BNaNTest(submitter)); printTestOutput("emulatedFloat64BInfTest", emulatedFloat64OneValIsZeroTest(submitter)); printTestOutput("emulatedFloat64BNegInfTest", emulatedFloat64OneValIsNegZeroTest(submitter)); + + m_logFile.close(); } template @@ -928,9 +927,10 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso { IAssetLoader::SAssetLoadParams lp = {}; lp.logger = base.m_logger.get(); - lp.workingDirectory = ""; // virtual root + lp.workingDirectory = "app_resources"; // virtual root // this time we load a shader directly from a file - auto assetBundle = base.m_assetMgr->getAsset("app_resources/benchmark/benchmark.comp.hlsl", lp); + auto key = nbl::this_example::builtin::build::get_spirv_key<"benchmark">(m_device.get()); + auto assetBundle = base.m_assetMgr->getAsset(key.data(), lp); const auto assets = assetBundle.getContents(); if (assets.empty()) { @@ -940,26 +940,11 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso // It would be super weird if loading a shader from a file produced more than 1 asset assert(assets.size() == 1); - smart_refctd_ptr source = IAsset::castDown(assets[0]); - - auto* compilerSet = base.m_assetMgr->getCompilerSet(); - - IShaderCompiler::SCompilerOptions options = {}; - options.stage = ESS_COMPUTE; - options.preprocessorOptions.targetSpirvVersion = base.m_device->getPhysicalDevice()->getLimits().spirvVersion; - options.spirvOptimizer = nullptr; - options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_SOURCE_BIT; - options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); - options.preprocessorOptions.logger = base.m_logger.get(); - options.preprocessorOptions.includeFinder = compilerSet->getShaderCompiler(source->getContentType())->getDefaultIncludeFinder(); - - auto spirv = compilerSet->compileToSPIRV(source.get(), options); - - shader = base.m_device->compileShader({spirv.get()}); + shader = IAsset::castDown(assets[0]); } if (!shader) - base.logFail("Failed to create a GPU Shader, seems the Driver doesn't like the SPIR-V we're feeding it!\n"); + base.logFail("Failed to load precompiled \"benchmark\" shader!\n"); nbl::video::IGPUDescriptorSetLayout::SBinding bindings[1] = { { @@ -1199,6 +1184,8 @@ class CompatibilityTest final : public MonoDeviceApplication, public BuiltinReso m_logger->log(msg, ILogger::ELL_ERROR, std::forward(args)...); return false; } + + std::ofstream m_logFile; }; NBL_MAIN_FUNC(CompatibilityTest) \ No newline at end of file diff --git a/66_HLSLBxDFTests/app_resources/tests.hlsl b/66_HLSLBxDFTests/app_resources/tests.hlsl index 256ed3ce9..6f67c359f 100644 --- a/66_HLSLBxDFTests/app_resources/tests.hlsl +++ b/66_HLSLBxDFTests/app_resources/tests.hlsl @@ -356,13 +356,13 @@ struct is_microfacet_bsdf : bool_constant< > {}; template -NBL_CONSTEXPR bool is_basic_brdf_v = is_basic_brdf::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_basic_brdf_v = is_basic_brdf::value; template -NBL_CONSTEXPR bool is_microfacet_brdf_v = is_microfacet_brdf::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_microfacet_brdf_v = is_microfacet_brdf::value; template -NBL_CONSTEXPR bool is_basic_bsdf_v = is_basic_bsdf::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_basic_bsdf_v = is_basic_bsdf::value; template -NBL_CONSTEXPR bool is_microfacet_bsdf_v = is_microfacet_bsdf::value; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR bool is_microfacet_bsdf_v = is_microfacet_bsdf::value; template diff --git a/67_RayQueryGeometry/CMakeLists.txt b/67_RayQueryGeometry/CMakeLists.txt index d26a90205..1fdfc03ce 100644 --- a/67_RayQueryGeometry/CMakeLists.txt +++ b/67_RayQueryGeometry/CMakeLists.txt @@ -25,4 +25,49 @@ if(NBL_EMBED_BUILTIN_RESOURCES) ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) -endif() \ No newline at end of file +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/common.hlsl + app_resources/render.comp.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/render.comp.hlsl", + "KEY": "render", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) \ No newline at end of file diff --git a/67_RayQueryGeometry/app_resources/common.hlsl b/67_RayQueryGeometry/app_resources/common.hlsl index 68a353adc..ecac0f59d 100644 --- a/67_RayQueryGeometry/app_resources/common.hlsl +++ b/67_RayQueryGeometry/app_resources/common.hlsl @@ -3,7 +3,7 @@ #include "nbl/builtin/hlsl/cpp_compat.hlsl" -NBL_CONSTEXPR uint32_t WorkgroupSize = 16; +NBL_CONSTEXPR_INLINE_NSPC_SCOPE_VAR uint32_t WorkgroupSize = 16; enum NormalType : uint32_t { diff --git a/67_RayQueryGeometry/app_resources/render.comp.hlsl b/67_RayQueryGeometry/app_resources/render.comp.hlsl index 954598c9a..889e1f38b 100644 --- a/67_RayQueryGeometry/app_resources/render.comp.hlsl +++ b/67_RayQueryGeometry/app_resources/render.comp.hlsl @@ -1,7 +1,5 @@ #include "common.hlsl" -#include "nbl/builtin/hlsl/jit/device_capabilities.hlsl" - #include "nbl/builtin/hlsl/glsl_compat/core.hlsl" #include "nbl/builtin/hlsl/spirv_intrinsics/raytracing.hlsl" #include "nbl/builtin/hlsl/bda/__ptr.hlsl" diff --git a/67_RayQueryGeometry/main.cpp b/67_RayQueryGeometry/main.cpp index 2783385f2..b35000485 100644 --- a/67_RayQueryGeometry/main.cpp +++ b/67_RayQueryGeometry/main.cpp @@ -2,6 +2,7 @@ // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h #include "common.hpp" +#include "nbl/this_example/builtin/build/spirv/keys.hpp" class RayQueryGeometryApp final : public SimpleWindowedApplication, public BuiltinResourcesApplication { @@ -150,8 +151,10 @@ class RayQueryGeometryApp final : public SimpleWindowedApplication, public Built const std::string shaderPath = "app_resources/render.comp.hlsl"; IAssetLoader::SAssetLoadParams lparams = {}; lparams.logger = m_logger.get(); - lparams.workingDirectory = ""; - auto bundle = m_assetMgr->getAsset(shaderPath, lparams); + lparams.workingDirectory = "app_resources"; + + auto key = nbl::this_example::builtin::build::get_spirv_key<"render">(m_device.get()); + auto bundle = m_assetMgr->getAsset(key.data(), lparams); if (bundle.getContents().empty() || bundle.getAssetType() != IAsset::ET_SHADER) { m_logger->log("Shader %s not found!", ILogger::ELL_ERROR, shaderPath); @@ -160,10 +163,9 @@ class RayQueryGeometryApp final : public SimpleWindowedApplication, public Built const auto assets = bundle.getContents(); assert(assets.size() == 1); - smart_refctd_ptr shaderSrc = IAsset::castDown(assets[0]); - auto shader = m_device->compileShader({shaderSrc.get()}); + smart_refctd_ptr shader = IAsset::castDown(assets[0]); if (!shader) - return logFail("Failed to create shader!"); + return logFail("Failed to load precompiled shader!"); SPushConstantRange pcRange = { .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, .offset = 0u, .size = sizeof(SPushConstants)}; auto pipelineLayout = m_device->createPipelineLayout({ &pcRange, 1 }, smart_refctd_ptr(renderDs->getLayout()), nullptr, nullptr, nullptr); diff --git a/70_FLIPFluids/CMakeLists.txt b/70_FLIPFluids/CMakeLists.txt index a434ff32a..842492167 100644 --- a/70_FLIPFluids/CMakeLists.txt +++ b/70_FLIPFluids/CMakeLists.txt @@ -21,4 +21,100 @@ if(NBL_EMBED_BUILTIN_RESOURCES) ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) -endif() \ No newline at end of file +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/compute/advectParticles.comp.hlsl + app_resources/compute/applyBodyForces.comp.hlsl + app_resources/compute/diffusion.comp.hlsl + app_resources/compute/genParticleVertices.comp.hlsl + app_resources/compute/particlesInit.comp.hlsl + app_resources/compute/prepareCellUpdate.comp.hlsl + app_resources/compute/pressureSolver.comp.hlsl + app_resources/compute/updateFluidCells.comp.hlsl + app_resources/cellUtils.hlsl + app_resources/common.hlsl + app_resources/descriptor_bindings.hlsl + app_resources/fluidParticles.fragment.hlsl + app_resources/fluidParticles.vertex.hlsl + app_resources/gridSampling.hlsl + app_resources/gridUtils.hlsl + app_resources/render_common.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/compute/diffusion.comp.hlsl", + "KEY": "diffusion", + }, + { + "INPUT": "app_resources/fluidParticles.vertex.hlsl", + "KEY": "fluidParticles_vertex", + }, + { + "INPUT": "app_resources/fluidParticles.fragment.hlsl", + "KEY": "fluidParticles_fragment", + }, + { + "INPUT": "app_resources/compute/particlesInit.comp.hlsl", + "KEY": "particlesInit", + }, + { + "INPUT": "app_resources/compute/genParticleVertices.comp.hlsl", + "KEY": "genParticleVertices", + }, + { + "INPUT": "app_resources/compute/prepareCellUpdate.comp.hlsl", + "KEY": "prepareCellUpdate", + }, + { + "INPUT": "app_resources/compute/updateFluidCells.comp.hlsl", + "KEY": "updateFluidCells", + }, + { + "INPUT": "app_resources/compute/applyBodyForces.comp.hlsl", + "KEY": "applyBodyForces", + }, + { + "INPUT": "app_resources/compute/pressureSolver.comp.hlsl", + "KEY": "pressureSolver", + }, + { + "INPUT": "app_resources/compute/advectParticles.comp.hlsl", + "KEY": "advectParticles", + } + +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) \ No newline at end of file diff --git a/70_FLIPFluids/app_resources/compute/diffusion.comp.hlsl b/70_FLIPFluids/app_resources/compute/diffusion.comp.hlsl index e53c91d2d..288b82764 100644 --- a/70_FLIPFluids/app_resources/compute/diffusion.comp.hlsl +++ b/70_FLIPFluids/app_resources/compute/diffusion.comp.hlsl @@ -67,6 +67,7 @@ void setAxisCellMaterial(uint32_t3 ID : SV_DispatchThreadID) } [numthreads(WorkgroupGridDim, WorkgroupGridDim, WorkgroupGridDim)] +[shader("compute")] void setNeighborAxisCellMaterial(uint32_t3 ID : SV_DispatchThreadID) { int3 cellIdx = ID; @@ -127,6 +128,7 @@ float3 calculateDiffusionVelStep(int3 idx, float3 sampledVelocity, uint cellMate } [numthreads(WorkgroupGridDim, WorkgroupGridDim, WorkgroupGridDim)] +[shader("compute")] void iterateDiffusion(uint32_t3 ID : SV_DispatchThreadID) { uint3 gid = nbl::hlsl::glsl::gl_WorkGroupID(); @@ -212,6 +214,7 @@ void iterateDiffusion(uint32_t3 ID : SV_DispatchThreadID) // TODO: same as the pressure solver, this kernel/dispatch should be fused onto `iterateDiffusion` guarded by `isLastIteration` push constant [numthreads(WorkgroupGridDim, WorkgroupGridDim, WorkgroupGridDim)] +[shader("compute")] void applyDiffusion(uint32_t3 ID : SV_DispatchThreadID) { int3 cellIdx = ID; diff --git a/70_FLIPFluids/app_resources/compute/pressureSolver.comp.hlsl b/70_FLIPFluids/app_resources/compute/pressureSolver.comp.hlsl index b5db995c5..e71f05912 100644 --- a/70_FLIPFluids/app_resources/compute/pressureSolver.comp.hlsl +++ b/70_FLIPFluids/app_resources/compute/pressureSolver.comp.hlsl @@ -89,6 +89,7 @@ float calculatePressureStep(int3 idx) } [numthreads(WorkgroupGridDim, WorkgroupGridDim, WorkgroupGridDim)] +[shader("compute")] void iteratePressureSystem(uint32_t3 ID : SV_DispatchThreadID) { uint3 gid = nbl::hlsl::glsl::gl_WorkGroupID(); @@ -168,6 +169,7 @@ void iteratePressureSystem(uint32_t3 ID : SV_DispatchThreadID) // TODO: why doesn't the last invocation of `iteratePressureSystem` have this step fused into it!? It would be just a simple push constant `isLastIteration` that would decide whether to run this dispatch [numthreads(WorkgroupGridDim, WorkgroupGridDim, WorkgroupGridDim)] +[shader("compute")] void updateVelocities(uint32_t3 ID : SV_DispatchThreadID) { int3 cellIdx = ID; diff --git a/70_FLIPFluids/app_resources/compute/updateFluidCells.comp.hlsl b/70_FLIPFluids/app_resources/compute/updateFluidCells.comp.hlsl index 62ddfd822..ea37660c1 100644 --- a/70_FLIPFluids/app_resources/compute/updateFluidCells.comp.hlsl +++ b/70_FLIPFluids/app_resources/compute/updateFluidCells.comp.hlsl @@ -23,6 +23,7 @@ cbuffer GridData // TODO: f 0 is AIR, and >=2 is SOLID, we can perform Atomic OR 0b01 to have a particle set the cell to FLUID, and this dispatch looping over all grid cells is not needed! [numthreads(WorkgroupGridDim, WorkgroupGridDim, WorkgroupGridDim)] +[shader("compute")] void updateFluidCells(uint32_t3 ID : SV_DispatchThreadID) { int3 cIdx = ID; diff --git a/70_FLIPFluids/main.cpp b/70_FLIPFluids/main.cpp index 899d00ba4..a70064245 100644 --- a/70_FLIPFluids/main.cpp +++ b/70_FLIPFluids/main.cpp @@ -2,6 +2,7 @@ // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#include "nbl/this_example/builtin/build/spirv/keys.hpp" #include "nbl/examples/examples.hpp" // TODO: why is it not in nabla.h ? @@ -344,11 +345,12 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso if (!initGraphicsPipeline()) return logFail("Failed to initialize render pipeline!\n"); - auto createComputePipeline = [&](smart_refctd_ptr& pipeline, smart_refctd_ptr& pool, - smart_refctd_ptr& set, const std::string& shaderPath, const std::string& entryPoint, + + auto createComputePipeline = [&](smart_refctd_ptr& pipeline, smart_refctd_ptr& pool, + smart_refctd_ptr& set, const std::string& entryPoint, const std::span bindings, const asset::SPushConstantRange& pcRange = {}) -> void { - auto shader = compileShader(shaderPath, entryPoint); + auto shader = loadPrecompiledShader(); auto descriptorSetLayout1 = m_device->createDescriptorSetLayout(bindings); @@ -378,8 +380,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso { // init particles pipeline const asset::SPushConstantRange pcRange = { .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, .offset = 0, .size = 2 * sizeof(uint64_t) }; - createComputePipeline(m_initParticlePipeline, m_initParticlePool, m_initParticleDs, - "app_resources/compute/particlesInit.comp.hlsl", "main", piParticlesInit_bs1, pcRange); + createComputePipeline.operator()<"particlesInit">(m_initParticlePipeline, m_initParticlePool, m_initParticleDs, + "main", piParticlesInit_bs1, pcRange); { IGPUDescriptorSet::SDescriptorInfo infos[1]; @@ -395,8 +397,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso { // generate particle vertex pipeline const asset::SPushConstantRange pcRange = { .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, .offset = 0, .size = 3 * sizeof(uint64_t) }; - createComputePipeline(m_genParticleVerticesPipeline, m_genVerticesPool, m_genVerticesDs, - "app_resources/compute/genParticleVertices.comp.hlsl", "main", gpvGenVertices_bs1, pcRange); + createComputePipeline.operator()<"genParticleVertices">(m_genParticleVerticesPipeline, m_genVerticesPool, m_genVerticesDs, + "main", gpvGenVertices_bs1, pcRange); { IGPUDescriptorSet::SDescriptorInfo infos[2]; @@ -414,8 +416,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso // update fluid cells pipelines { const asset::SPushConstantRange pcRange = { .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, .offset = 0, .size = 2 * sizeof(uint64_t) }; - createComputePipeline(m_accumulateWeightsPipeline, m_accumulateWeightsPool, m_accumulateWeightsDs, - "app_resources/compute/prepareCellUpdate.comp.hlsl", "main", ufcAccWeights_bs1, pcRange); + createComputePipeline.operator()<"prepareCellUpdate">(m_accumulateWeightsPipeline, m_accumulateWeightsPool, m_accumulateWeightsDs, + "main", ufcAccWeights_bs1, pcRange); { IGPUDescriptorSet::SDescriptorInfo infos[2]; @@ -457,8 +459,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso } } { - createComputePipeline(m_updateFluidCellsPipeline, m_updateFluidCellsPool, m_updateFluidCellsDs, - "app_resources/compute/updateFluidCells.comp.hlsl", "updateFluidCells", ufcFluidCell_bs1); + createComputePipeline.operator()<"updateFluidCells">(m_updateFluidCellsPipeline, m_updateFluidCellsPool, m_updateFluidCellsDs, + "updateFluidCells", ufcFluidCell_bs1); { IGPUDescriptorSet::SDescriptorInfo infos[3]; @@ -479,8 +481,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso } } { - createComputePipeline(m_updateNeighborCellsPipeline, m_updateNeighborCellsPool, m_updateNeighborCellsDs, - "app_resources/compute/updateFluidCells.comp.hlsl", "updateNeighborFluidCells", ufcNeighborCell_bs1); + createComputePipeline.operator()<"updateFluidCells">(m_updateNeighborCellsPipeline, m_updateNeighborCellsPool, m_updateNeighborCellsDs, + "updateNeighborFluidCells", ufcNeighborCell_bs1); { IGPUDescriptorSet::SDescriptorInfo infos[3]; @@ -527,8 +529,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso } { // apply forces pipeline - createComputePipeline(m_applyBodyForcesPipeline, m_applyForcesPool, m_applyForcesDs, - "app_resources/compute/applyBodyForces.comp.hlsl", "main", abfApplyForces_bs1); + createComputePipeline.operator()<"applyBodyForces">(m_applyBodyForcesPipeline, m_applyForcesPool, m_applyForcesDs, + "main", abfApplyForces_bs1); { IGPUDescriptorSet::SDescriptorInfo infos[2]; @@ -559,8 +561,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso } // apply diffusion pipelines { - createComputePipeline(m_axisCellsPipeline, m_axisCellsPool, m_axisCellsDs, - "app_resources/compute/diffusion.comp.hlsl", "setAxisCellMaterial", dAxisCM_bs1); + createComputePipeline.operator()<"diffusion">(m_axisCellsPipeline, m_axisCellsPool, m_axisCellsDs, + "setAxisCellMaterial", dAxisCM_bs1); { IGPUDescriptorSet::SDescriptorInfo infos[3]; @@ -581,8 +583,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso } } { - createComputePipeline(m_neighborAxisCellsPipeline, m_neighborAxisCellsPool, m_neighborAxisCellsDs, - "app_resources/compute/diffusion.comp.hlsl", "setNeighborAxisCellMaterial", dNeighborAxisCM_bs1); + createComputePipeline.operator()<"diffusion">(m_neighborAxisCellsPipeline, m_neighborAxisCellsPool, m_neighborAxisCellsDs, + "setNeighborAxisCellMaterial", dNeighborAxisCM_bs1); { IGPUDescriptorSet::SDescriptorInfo infos[3]; @@ -603,10 +605,7 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso } } { - const std::string iterateKernel = "iterateDiffusion"; - const std::string applyKernel = "applyDiffusion"; - auto iterateShader = compileShader("app_resources/compute/diffusion.comp.hlsl", iterateKernel); - auto applyShader = compileShader("app_resources/compute/diffusion.comp.hlsl", applyKernel); + smart_refctd_ptr diffusion = loadPrecompiledShader<"diffusion">(); // "app_resources/compute/diffusion.comp.hlsl" auto descriptorSetLayout1 = m_device->createDescriptorSetLayout(dDiffuse_bs1); @@ -625,16 +624,16 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso { IGPUComputePipeline::SCreationParams params = {}; params.layout = pipelineLayout.get(); - params.shader.entryPoint = iterateKernel; - params.shader.shader = iterateShader.get(); + params.shader.entryPoint = "iterateDiffusion"; + params.shader.shader = diffusion.get(); m_device->createComputePipelines(nullptr, { ¶ms,1 }, &m_iterateDiffusionPipeline); } { IGPUComputePipeline::SCreationParams params = {}; params.layout = pipelineLayout.get(); - params.shader.entryPoint = applyKernel; - params.shader.shader = applyShader.get(); + params.shader.entryPoint = "applyDiffusion"; + params.shader.shader = diffusion.get(); m_device->createComputePipelines(nullptr, { ¶ms,1 }, &m_diffusionPipeline); } @@ -676,8 +675,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso } // solve pressure system pipelines { - createComputePipeline(m_calcDivergencePipeline, m_calcDivergencePool, m_calcDivergenceDs, - "app_resources/compute/pressureSolver.comp.hlsl", "calculateNegativeDivergence", psDivergence_bs1); + createComputePipeline.operator()<"pressureSolver">(m_calcDivergencePipeline, m_calcDivergencePool, m_calcDivergenceDs, + "calculateNegativeDivergence", psDivergence_bs1); { IGPUDescriptorSet::SDescriptorInfo infos[3]; @@ -711,8 +710,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso } } { - createComputePipeline(m_iteratePressurePipeline, m_iteratePressurePool, m_iteratePressureDs, - "app_resources/compute/pressureSolver.comp.hlsl", "iteratePressureSystem", psIteratePressure_bs1); + createComputePipeline.operator()<"pressureSolver">(m_iteratePressurePipeline, m_iteratePressurePool, m_iteratePressureDs, + "iteratePressureSystem", psIteratePressure_bs1); { IGPUDescriptorSet::SDescriptorInfo infos[5]; @@ -740,8 +739,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso } } { - createComputePipeline(m_updateVelPsPipeline, m_updateVelPsPool, m_updateVelPsDs, - "app_resources/compute/pressureSolver.comp.hlsl", "updateVelocities", psUpdateVelPs_bs1); + createComputePipeline.operator()<"pressureSolver">(m_updateVelPsPipeline, m_updateVelPsPool, m_updateVelPsDs, + "updateVelocities", psUpdateVelPs_bs1); { IGPUDescriptorSet::SDescriptorInfo infos[4]; @@ -780,8 +779,8 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso { // advect particles pipeline const asset::SPushConstantRange pcRange = { .stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE, .offset = 0, .size = 2 * sizeof(uint64_t) }; - createComputePipeline(m_advectParticlesPipeline, m_advectParticlesPool, m_advectParticlesDs, - "app_resources/compute/advectParticles.comp.hlsl", "main", apAdvectParticles_bs1, pcRange); + createComputePipeline.operator()<"advectParticles">(m_advectParticlesPipeline, m_advectParticlesPool, m_advectParticlesDs, + "main", apAdvectParticles_bs1, pcRange); { IGPUDescriptorSet::SDescriptorInfo infos[2]; @@ -1400,51 +1399,25 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso numParticles = m_gridData.particleInitSize.x * m_gridData.particleInitSize.y * m_gridData.particleInitSize.z * particlesPerCell; } - smart_refctd_ptr compileShader(const std::string& filePath, const std::string& entryPoint = "main") + template + smart_refctd_ptr loadPrecompiledShader() { IAssetLoader::SAssetLoadParams lparams = {}; lparams.logger = m_logger.get(); - lparams.workingDirectory = ""; - auto bundle = m_assetMgr->getAsset(filePath, lparams); + lparams.workingDirectory = "app_resources"; + auto key = nbl::this_example::builtin::build::get_spirv_key(m_device.get()); + auto bundle = m_assetMgr->getAsset(key.data(), lparams); if (bundle.getContents().empty() || bundle.getAssetType() != IAsset::ET_SHADER) { - m_logger->log("Shader %s not found!", ILogger::ELL_ERROR, filePath); + m_logger->log("Failed to find shader with key '%s'.", ILogger::ELL_ERROR, ShaderKey); exit(-1); } const auto assets = bundle.getContents(); assert(assets.size() == 1); - smart_refctd_ptr shaderSrc = IAsset::castDown(assets[0]); - const auto hlslMetadata = static_cast(bundle.getMetadata()); - const auto shaderStage = hlslMetadata->shaderStages->front(); + smart_refctd_ptr shader = IAsset::castDown(assets[0]); - smart_refctd_ptr shader = shaderSrc; - if (entryPoint != "main") - { - auto compiler = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); - CHLSLCompiler::SOptions options = {}; - options.stage = shaderStage; - if (!(options.stage == IShader::E_SHADER_STAGE::ESS_COMPUTE || options.stage == IShader::E_SHADER_STAGE::ESS_FRAGMENT)) - options.stage = IShader::E_SHADER_STAGE::ESS_VERTEX; - options.preprocessorOptions.targetSpirvVersion = m_device->getPhysicalDevice()->getLimits().spirvVersion; - options.spirvOptimizer = nullptr; - #ifndef _NBL_DEBUG - ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; - auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); - options.spirvOptimizer = opt.get(); - #endif - options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_SOURCE_BIT; - options.preprocessorOptions.sourceIdentifier = shaderSrc->getFilepathHint(); - options.preprocessorOptions.logger = m_logger.get(); - options.preprocessorOptions.includeFinder = compiler->getDefaultIncludeFinder(); - - std::string dxcOptionStr[] = {"-E " + entryPoint}; - options.dxcOptions = std::span(dxcOptionStr); - - shader = compiler->compileToSPIRV((const char*)shaderSrc->getContent()->getPointer(), options); - } - - return m_device->compileShader({ shader.get() }); + return shader; } // TODO: there's a method in IUtilities for this @@ -1563,28 +1536,27 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso // init shaders and pipeline - auto compileShader = [&](const std::string& filePath) -> smart_refctd_ptr + auto loadPrecompiledShader = [&]() -> smart_refctd_ptr + { + IAssetLoader::SAssetLoadParams lparams = {}; + lparams.logger = m_logger.get(); + lparams.workingDirectory = "app_resources"; + auto key = nbl::this_example::builtin::build::get_spirv_key(m_device.get()); + auto bundle = m_assetMgr->getAsset(key.data(), lparams); + if (bundle.getContents().empty() || bundle.getAssetType() != IAsset::ET_SHADER) { - IAssetLoader::SAssetLoadParams lparams = {}; - lparams.logger = m_logger.get(); - lparams.workingDirectory = ""; - auto bundle = m_assetMgr->getAsset(filePath, lparams); - if (bundle.getContents().empty() || bundle.getAssetType() != IAsset::ET_SHADER) - { - m_logger->log("Shader %s not found!", ILogger::ELL_ERROR, filePath); - exit(-1); - } + m_logger->log("Failed to find shader with key '%s'.", ILogger::ELL_ERROR, ShaderKey); + exit(-1); + } - const auto assets = bundle.getContents(); - assert(assets.size() == 1); - smart_refctd_ptr shaderSrc = IAsset::castDown(assets[0]); - if (!shaderSrc) - return nullptr; + const auto assets = bundle.getContents(); + assert(assets.size() == 1); + smart_refctd_ptr shader = IAsset::castDown(assets[0]); - return m_device->compileShader({ shaderSrc.get() }); - }; - auto vs = compileShader("app_resources/fluidParticles.vertex.hlsl"); - auto fs = compileShader("app_resources/fluidParticles.fragment.hlsl"); + return shader; + }; + auto vs = loadPrecompiledShader.operator()<"fluidParticles_vertex">(); // "app_resources/fluidParticles.vertex.hlsl" + auto fs = loadPrecompiledShader.operator()<"fluidParticles_fragment">(); // "app_resources/fluidParticles.fragment.hlsl" smart_refctd_ptr descriptorSetLayout1; { diff --git a/71_RayTracingPipeline/CMakeLists.txt b/71_RayTracingPipeline/CMakeLists.txt index 07b0fd396..d7bb13671 100644 --- a/71_RayTracingPipeline/CMakeLists.txt +++ b/71_RayTracingPipeline/CMakeLists.txt @@ -34,4 +34,104 @@ if(NBL_BUILD_IMGUI) endif() endif() +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") +set(DEPENDS + app_resources/common.hlsl + app_resources/light_directional.rcall.hlsl + app_resources/light_point.rcall.hlsl + app_resources/light_spot.rcall.hlsl + app_resources/present.frag.hlsl + app_resources/raytrace.rahit.hlsl + app_resources/raytrace.rchit.hlsl + app_resources/raytrace.rgen.hlsl + app_resources/raytrace.rint.hlsl + app_resources/raytrace.rmiss.hlsl + app_resources/raytrace_procedural.rchit.hlsl + app_resources/raytrace_shadow.rahit.hlsl + app_resources/raytrace_shadow.rmiss.hlsl +) +target_sources(${EXECUTABLE_NAME} PRIVATE ${DEPENDS}) +set_source_files_properties(${DEPENDS} PROPERTIES HEADER_FILE_ONLY ON) + +set(SM 6_8) +set(JSON [=[ +[ + { + "INPUT": "app_resources/raytrace.rgen.hlsl", + "KEY": "raytrace_rgen", + }, + { + "INPUT": "app_resources/raytrace.rchit.hlsl", + "KEY": "raytrace_rchit", + }, + { + "INPUT": "app_resources/raytrace_procedural.rchit.hlsl", + "KEY": "raytrace_procedural_rchit", + }, + { + "INPUT": "app_resources/raytrace.rint.hlsl", + "KEY": "raytrace_rint", + }, + { + "INPUT": "app_resources/raytrace.rahit.hlsl", + "KEY": "raytrace_rahit", + }, + { + "INPUT": "app_resources/raytrace_shadow.rahit.hlsl", + "KEY": "raytrace_shadow_rahit", + }, + { + "INPUT": "app_resources/raytrace.rmiss.hlsl", + "KEY": "raytrace_rmiss", + }, + { + "INPUT": "app_resources/raytrace_shadow.rmiss.hlsl", + "KEY": "raytrace_shadow_rmiss", + }, + { + "INPUT": "app_resources/light_directional.rcall.hlsl", + "KEY": "light_directional_rcall", + }, + { + "INPUT": "app_resources/light_point.rcall.hlsl", + "KEY": "light_point_rcall", + }, + { + "INPUT": "app_resources/light_spot.rcall.hlsl", + "KEY": "light_spot_rcall", + }, + { + "INPUT": "app_resources/present.frag.hlsl", + "KEY": "present_frag", + } +] +]=]) +string(CONFIGURE "${JSON}" JSON) + +set(COMPILE_OPTIONS + -I "${CMAKE_CURRENT_SOURCE_DIR}" + -T lib_${SM} +) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + DEPENDS ${DEPENDS} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS ${COMPILE_OPTIONS} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) + +NBL_CREATE_RESOURCE_ARCHIVE( + NAMESPACE nbl::this_example::builtin::build + TARGET ${EXECUTABLE_NAME}_builtinsBuild + LINK_TO ${EXECUTABLE_NAME} + BIND ${OUTPUT_DIRECTORY} + BUILTINS ${KEYS} +) + diff --git a/71_RayTracingPipeline/app_resources/common.hlsl b/71_RayTracingPipeline/app_resources/common.hlsl index f9d67af78..502b53160 100644 --- a/71_RayTracingPipeline/app_resources/common.hlsl +++ b/71_RayTracingPipeline/app_resources/common.hlsl @@ -4,6 +4,7 @@ #include "nbl/builtin/hlsl/cpp_compat.hlsl" #include "nbl/builtin/hlsl/cpp_compat/basic.h" #include "nbl/builtin/hlsl/random/pcg.hlsl" +#include "nbl/builtin/hlsl/type_traits.hlsl" NBL_CONSTEXPR uint32_t WorkgroupSize = 16; NBL_CONSTEXPR uint32_t MAX_UNORM_10 = 1023; @@ -78,6 +79,9 @@ struct MaterialPacked return (xi>>22) > alpha; } }; +#ifdef __HLSL_VERSION +NBL_REGISTER_OBJ_TYPE(MaterialPacked, 4) +#endif struct SProceduralGeomInfo { @@ -103,6 +107,9 @@ struct STriangleGeomInfo uint32_t indexType : 1; // 16 bit, 32 bit }; +#ifdef __HLSL_VERSION +NBL_REGISTER_OBJ_TYPE(STriangleGeomInfo, 8) +#endif enum E_GEOM_TYPE : uint16_t { diff --git a/71_RayTracingPipeline/app_resources/raytrace.rahit.hlsl b/71_RayTracingPipeline/app_resources/raytrace.rahit.hlsl index 956ad5fe6..da7cc1594 100644 --- a/71_RayTracingPipeline/app_resources/raytrace.rahit.hlsl +++ b/71_RayTracingPipeline/app_resources/raytrace.rahit.hlsl @@ -10,7 +10,8 @@ using namespace nbl::hlsl; void main(inout PrimaryPayload payload, in BuiltInTriangleIntersectionAttributes attribs) { const int instID = spirv::InstanceCustomIndexKHR; - const STriangleGeomInfo geom = vk::RawBufferLoad < STriangleGeomInfo > (pc.triangleGeomInfoBuffer + instID * sizeof(STriangleGeomInfo)); + const static uint64_t STriangleGeomInfoAlignment = nbl::hlsl::alignment_of_v; + const STriangleGeomInfo geom = vk::BufferPointer(pc.triangleGeomInfoBuffer + instID * sizeof(STriangleGeomInfo)).Get(); const uint32_t bitpattern = payload.pcg(); // Cannot use spirv::ignoreIntersectionKHR and spirv::terminateRayKHR due to https://github.com/microsoft/DirectXShaderCompiler/issues/7279 diff --git a/71_RayTracingPipeline/app_resources/raytrace.rchit.hlsl b/71_RayTracingPipeline/app_resources/raytrace.rchit.hlsl index 0a8bc5ec8..e6ebcda78 100644 --- a/71_RayTracingPipeline/app_resources/raytrace.rchit.hlsl +++ b/71_RayTracingPipeline/app_resources/raytrace.rchit.hlsl @@ -38,9 +38,9 @@ float3 calculateNormals(int primID, STriangleGeomInfo geom, float2 bary) if (normalBufferAddress == 0) { - float3 v0 = vk::RawBufferLoad(vertexBufferAddress + indices[0] * 12); - float3 v1 = vk::RawBufferLoad(vertexBufferAddress + indices[1] * 12); - float3 v2 = vk::RawBufferLoad(vertexBufferAddress + indices[2] * 12); + float3 v0 = (nbl::hlsl::bda::__ptr::create(vertexBufferAddress) + indices[0]).deref().load(); + float3 v1 = (nbl::hlsl::bda::__ptr::create(vertexBufferAddress) + indices[1]).deref().load(); + float3 v2 = (nbl::hlsl::bda::__ptr::create(vertexBufferAddress) + indices[2]).deref().load(); return normalize(cross(v2 - v0, v1 - v0)); } @@ -50,9 +50,9 @@ float3 calculateNormals(int primID, STriangleGeomInfo geom, float2 bary) { case NT_R8G8B8A8_SNORM: { - uint32_t v0 = vk::RawBufferLoad(normalBufferAddress + indices[0] * 4); - uint32_t v1 = vk::RawBufferLoad(normalBufferAddress + indices[1] * 4); - uint32_t v2 = vk::RawBufferLoad(normalBufferAddress + indices[2] * 4); + uint32_t v0 = (nbl::hlsl::bda::__ptr::create(normalBufferAddress) + indices[0]).deref().load(); + uint32_t v1 = (nbl::hlsl::bda::__ptr::create(normalBufferAddress) + indices[1]).deref().load(); + uint32_t v2 = (nbl::hlsl::bda::__ptr::create(normalBufferAddress) + indices[2]).deref().load(); n0 = normalize(nbl::hlsl::spirv::unpackSnorm4x8(v0).xyz); n1 = normalize(nbl::hlsl::spirv::unpackSnorm4x8(v1).xyz); @@ -61,9 +61,13 @@ float3 calculateNormals(int primID, STriangleGeomInfo geom, float2 bary) break; case NT_R32G32B32_SFLOAT: { - n0 = normalize(vk::RawBufferLoad(normalBufferAddress + indices[0] * 12)); - n1 = normalize(vk::RawBufferLoad(normalBufferAddress + indices[1] * 12)); - n2 = normalize(vk::RawBufferLoad(normalBufferAddress + indices[2] * 12)); + float3 v0 = (nbl::hlsl::bda::__ptr::create(normalBufferAddress) + indices[0]).deref().load(); + float3 v1 = (nbl::hlsl::bda::__ptr::create(normalBufferAddress) + indices[1]).deref().load(); + float3 v2 = (nbl::hlsl::bda::__ptr::create(normalBufferAddress) + indices[2]).deref().load(); + + n0 = normalize(v0); + n1 = normalize(v1); + n2 = normalize(v2); } break; } @@ -81,7 +85,8 @@ void main(inout PrimaryPayload payload, in BuiltInTriangleIntersectionAttributes const int primID = spirv::PrimitiveId; const int instanceCustomIndex = spirv::InstanceCustomIndexKHR; const int geometryIndex = spirv::RayGeometryIndexKHR; - const STriangleGeomInfo geom = vk::RawBufferLoad < STriangleGeomInfo > (pc.triangleGeomInfoBuffer + (instanceCustomIndex + geometryIndex) * sizeof(STriangleGeomInfo)); + const static uint64_t STriangleGeomInfoAlignment = nbl::hlsl::alignment_of_v; + const STriangleGeomInfo geom = vk::BufferPointer(pc.triangleGeomInfoBuffer + (instanceCustomIndex + geometryIndex) * sizeof(STriangleGeomInfo)).Get(); const float32_t3 vertexNormal = calculateNormals(primID, geom, attribs.barycentrics); const float32_t3 worldNormal = normalize(mul(vertexNormal, transpose(spirv::WorldToObjectKHR)).xyz); diff --git a/71_RayTracingPipeline/app_resources/raytrace.rgen.hlsl b/71_RayTracingPipeline/app_resources/raytrace.rgen.hlsl index efc99cad9..c42d5a7df 100644 --- a/71_RayTracingPipeline/app_resources/raytrace.rgen.hlsl +++ b/71_RayTracingPipeline/app_resources/raytrace.rgen.hlsl @@ -1,6 +1,5 @@ #include "common.hlsl" -#include "nbl/builtin/hlsl/jit/device_capabilities.hlsl" #include "nbl/builtin/hlsl/random/xoroshiro.hlsl" #include "nbl/builtin/hlsl/glsl_compat/core.hlsl" @@ -80,15 +79,16 @@ void main() Material material; MaterialId materialId = payload.materialId; + const static uint64_t MaterialPackedAlignment = nbl::hlsl::alignment_of_v; // we use negative index to indicate that this is a procedural geometry if (materialId.isHitProceduralGeom()) { - const MaterialPacked materialPacked = vk::RawBufferLoad(pc.proceduralGeomInfoBuffer + materialId.getMaterialIndex() * sizeof(SProceduralGeomInfo)); + const MaterialPacked materialPacked = vk::BufferPointer(pc.proceduralGeomInfoBuffer + materialId.getMaterialIndex() * sizeof(SProceduralGeomInfo)).Get(); material = nbl::hlsl::_static_cast(materialPacked); } else { - const MaterialPacked materialPacked = vk::RawBufferLoad(pc.triangleGeomInfoBuffer + materialId.getMaterialIndex() * sizeof(STriangleGeomInfo)); + const MaterialPacked materialPacked = vk::BufferPointer(pc.triangleGeomInfoBuffer + materialId.getMaterialIndex() * sizeof(STriangleGeomInfo)).Get(); material = nbl::hlsl::_static_cast(materialPacked); } diff --git a/71_RayTracingPipeline/app_resources/raytrace.rint.hlsl b/71_RayTracingPipeline/app_resources/raytrace.rint.hlsl index 72f9beffd..551be1c8a 100644 --- a/71_RayTracingPipeline/app_resources/raytrace.rint.hlsl +++ b/71_RayTracingPipeline/app_resources/raytrace.rint.hlsl @@ -36,8 +36,9 @@ void main() const int primID = spirv::PrimitiveId; + const static uint64_t SProceduralGeomInfoAlignment = nbl::hlsl::alignment_of_v; // Sphere data - SProceduralGeomInfo sphere = vk::RawBufferLoad(pc.proceduralGeomInfoBuffer + primID * sizeof(SProceduralGeomInfo)); + SProceduralGeomInfo sphere = vk::BufferPointer(pc.proceduralGeomInfoBuffer + primID * sizeof(SProceduralGeomInfo)).Get(); const float32_t tHit = hitSphere(sphere, ray); diff --git a/71_RayTracingPipeline/app_resources/raytrace_shadow.rahit.hlsl b/71_RayTracingPipeline/app_resources/raytrace_shadow.rahit.hlsl index e41551512..d87b8dd5d 100644 --- a/71_RayTracingPipeline/app_resources/raytrace_shadow.rahit.hlsl +++ b/71_RayTracingPipeline/app_resources/raytrace_shadow.rahit.hlsl @@ -1,6 +1,7 @@ #include "common.hlsl" #include "nbl/builtin/hlsl/spirv_intrinsics/raytracing.hlsl" #include "nbl/builtin/hlsl/spirv_intrinsics/core.hlsl" +#include "nbl/builtin/hlsl/type_traits.hlsl" using namespace nbl::hlsl; @@ -10,7 +11,8 @@ using namespace nbl::hlsl; void main(inout OcclusionPayload payload, in BuiltInTriangleIntersectionAttributes attribs) { const int instID = spirv::InstanceCustomIndexKHR; - const STriangleGeomInfo geom = vk::RawBufferLoad < STriangleGeomInfo > (pc.triangleGeomInfoBuffer + instID * sizeof(STriangleGeomInfo)); + const static uint64_t STriangleGeomInfoAlignment = nbl::hlsl::alignment_of_v; + const STriangleGeomInfo geom = vk::BufferPointer(pc.triangleGeomInfoBuffer + instID * sizeof(STriangleGeomInfo)).Get(); const Material material = nbl::hlsl::_static_cast(geom.material); const float attenuation = (1.f-material.alpha) * payload.attenuation; diff --git a/71_RayTracingPipeline/main.cpp b/71_RayTracingPipeline/main.cpp index 59b610f4b..ecaf53b7f 100644 --- a/71_RayTracingPipeline/main.cpp +++ b/71_RayTracingPipeline/main.cpp @@ -3,6 +3,8 @@ // For conditions of distribution and use, see copyright notice in nabla.h #include "common.hpp" +#include "nbl/this_example/builtin/build/spirv/keys.hpp" + #include "nbl/ext/FullScreenTriangle/FullScreenTriangle.h" #include "nbl/builtin/hlsl/indirect_commands.hlsl" @@ -106,95 +108,42 @@ class RaytracingPipelineApp final : public SimpleWindowedApplication, public Bui if (!asset_base_t::onAppInitialized(smart_refctd_ptr(system))) return false; - smart_refctd_ptr shaderReadCache = nullptr; - smart_refctd_ptr shaderWriteCache = core::make_smart_refctd_ptr(); - auto shaderCachePath = localOutputCWD / "main_pipeline_shader_cache.bin"; - - { - core::smart_refctd_ptr shaderReadCacheFile; - { - system::ISystem::future_t> future; - m_system->createFile(future, shaderCachePath.c_str(), system::IFile::ECF_READ); - if (future.wait()) - { - future.acquire().move_into(shaderReadCacheFile); - if (shaderReadCacheFile) - { - const size_t size = shaderReadCacheFile->getSize(); - if (size > 0ull) - { - std::vector contents(size); - system::IFile::success_t succ; - shaderReadCacheFile->read(succ, contents.data(), 0, size); - if (succ) - shaderReadCache = IShaderCompiler::CCache::deserialize(contents); - } - } - } - else - m_logger->log("Failed Openning Shader Cache File.", ILogger::ELL_ERROR); - } - - } - // Load Custom Shader - auto loadCompileAndCreateShader = [&](const std::string& relPath) -> smart_refctd_ptr + auto loadPrecompiledShader = [&]() -> smart_refctd_ptr { IAssetLoader::SAssetLoadParams lp = {}; lp.logger = m_logger.get(); - lp.workingDirectory = ""; // virtual root - auto assetBundle = m_assetMgr->getAsset(relPath, lp); + lp.workingDirectory = "app_resources"; // virtual root + auto key = nbl::this_example::builtin::build::get_spirv_key(m_device.get()); + auto assetBundle = m_assetMgr->getAsset(key.data(), lp); const auto assets = assetBundle.getContents(); if (assets.empty()) return nullptr; // lets go straight from ICPUSpecializedShader to IGPUSpecializedShader - auto sourceRaw = IAsset::castDown(assets[0]); - if (!sourceRaw) + auto shader = IAsset::castDown(assets[0]); + if (!shader) + { + m_logger->log("Failed to load a precompiled shader.", ILogger::ELL_ERROR); return nullptr; + } - return m_device->compileShader({ sourceRaw.get(), nullptr, shaderReadCache.get(), shaderWriteCache.get() }); + return shader; }; // load shaders - const auto raygenShader = loadCompileAndCreateShader("app_resources/raytrace.rgen.hlsl"); - const auto closestHitShader = loadCompileAndCreateShader("app_resources/raytrace.rchit.hlsl"); - const auto proceduralClosestHitShader = loadCompileAndCreateShader("app_resources/raytrace_procedural.rchit.hlsl"); - const auto intersectionHitShader = loadCompileAndCreateShader("app_resources/raytrace.rint.hlsl"); - const auto anyHitShaderColorPayload = loadCompileAndCreateShader("app_resources/raytrace.rahit.hlsl"); - const auto anyHitShaderShadowPayload = loadCompileAndCreateShader("app_resources/raytrace_shadow.rahit.hlsl"); - const auto missShader = loadCompileAndCreateShader("app_resources/raytrace.rmiss.hlsl"); - const auto missShadowShader = loadCompileAndCreateShader("app_resources/raytrace_shadow.rmiss.hlsl"); - const auto directionalLightCallShader = loadCompileAndCreateShader("app_resources/light_directional.rcall.hlsl"); - const auto pointLightCallShader = loadCompileAndCreateShader("app_resources/light_point.rcall.hlsl"); - const auto spotLightCallShader = loadCompileAndCreateShader("app_resources/light_spot.rcall.hlsl"); - const auto fragmentShader = loadCompileAndCreateShader("app_resources/present.frag.hlsl"); - - core::smart_refctd_ptr shaderWriteCacheFile; - { - system::ISystem::future_t> future; - m_system->deleteFile(shaderCachePath); // temp solution instead of trimming, to make sure we won't have corrupted json - m_system->createFile(future, shaderCachePath.c_str(), system::IFile::ECF_WRITE); - if (future.wait()) - { - future.acquire().move_into(shaderWriteCacheFile); - if (shaderWriteCacheFile) - { - auto serializedCache = shaderWriteCache->serialize(); - if (shaderWriteCacheFile) - { - system::IFile::success_t succ; - shaderWriteCacheFile->write(succ, serializedCache->getPointer(), 0, serializedCache->getSize()); - if (!succ) - m_logger->log("Failed Writing To Shader Cache File.", ILogger::ELL_ERROR); - } - } - else - m_logger->log("Failed Creating Shader Cache File.", ILogger::ELL_ERROR); - } - else - m_logger->log("Failed Creating Shader Cache File.", ILogger::ELL_ERROR); - } + const auto raygenShader = loadPrecompiledShader.operator()<"raytrace_rgen">(); // "app_resources/raytrace.rgen.hlsl" + const auto closestHitShader = loadPrecompiledShader.operator()<"raytrace_rchit">(); // "app_resources/raytrace.rchit.hlsl" + const auto proceduralClosestHitShader = loadPrecompiledShader.operator()<"raytrace_procedural_rchit">(); // "app_resources/raytrace_procedural.rchit.hlsl" + const auto intersectionHitShader = loadPrecompiledShader.operator()<"raytrace_rint">(); // "app_resources/raytrace.rint.hlsl" + const auto anyHitShaderColorPayload = loadPrecompiledShader.operator()<"raytrace_rahit">(); // "app_resources/raytrace.rahit.hlsl" + const auto anyHitShaderShadowPayload = loadPrecompiledShader.operator()<"raytrace_shadow_rahit">(); // "app_resources/raytrace_shadow.rahit.hlsl" + const auto missShader = loadPrecompiledShader.operator()<"raytrace_rmiss">(); // "app_resources/raytrace.rmiss.hlsl" + const auto missShadowShader = loadPrecompiledShader.operator()<"raytrace_shadow_rmiss">(); // "app_resources/raytrace_shadow.rmiss.hlsl" + const auto directionalLightCallShader = loadPrecompiledShader.operator()<"light_directional_rcall">(); // "app_resources/light_directional.rcall.hlsl" + const auto pointLightCallShader = loadPrecompiledShader.operator()<"light_point_rcall">(); // "app_resources/light_point.rcall.hlsl" + const auto spotLightCallShader = loadPrecompiledShader.operator()<"light_spot_rcall">(); // "app_resources/light_spot.rcall.hlsl" + const auto fragmentShader = loadPrecompiledShader.operator()<"present_frag">(); // "app_resources/present.frag.hlsl" m_semaphore = m_device->createSemaphore(m_realFrameIx); if (!m_semaphore) diff --git a/72_CooperativeBinarySearch/CMakeLists.txt b/72_CooperativeBinarySearch/CMakeLists.txt new file mode 100644 index 000000000..b7e52875d --- /dev/null +++ b/72_CooperativeBinarySearch/CMakeLists.txt @@ -0,0 +1,24 @@ +include(common RESULT_VARIABLE RES) +if(NOT RES) + message(FATAL_ERROR "common.cmake not found. Should be in {repo_root}/cmake directory") +endif() + +nbl_create_executable_project("" "" "" "" "${NBL_EXECUTABLE_PROJECT_CREATION_PCH_TARGET}") + +if(NBL_EMBED_BUILTIN_RESOURCES) + set(_BR_TARGET_ ${EXECUTABLE_NAME}_builtinResourceData) + set(RESOURCE_DIR "app_resources") + + get_filename_component(_SEARCH_DIRECTORIES_ "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) + get_filename_component(_OUTPUT_DIRECTORY_SOURCE_ "${CMAKE_CURRENT_BINARY_DIR}/src" ABSOLUTE) + get_filename_component(_OUTPUT_DIRECTORY_HEADER_ "${CMAKE_CURRENT_BINARY_DIR}/include" ABSOLUTE) + + file(GLOB_RECURSE BUILTIN_RESOURCE_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_DIR}" CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_DIR}/*") + foreach(RES_FILE ${BUILTIN_RESOURCE_FILES}) + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "${RES_FILE}") + endforeach() + + ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") + + LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) +endif() \ No newline at end of file diff --git a/72_CooperativeBinarySearch/app_resources/binarySearch.comp.hlsl b/72_CooperativeBinarySearch/app_resources/binarySearch.comp.hlsl new file mode 100644 index 000000000..0834e8f91 --- /dev/null +++ b/72_CooperativeBinarySearch/app_resources/binarySearch.comp.hlsl @@ -0,0 +1,120 @@ +// Copyright (C) 2024-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 +#pragma wave shader_stage(compute) + +#include "common.h" + +#include "nbl/builtin/hlsl/glsl_compat/subgroup_ballot.hlsl" + +using namespace nbl::hlsl; + +[[vk::push_constant]] PushConstants Constants; +[[vk::binding(0)]] StructuredBuffer Histogram; +[[vk::binding(1)]] RWStructuredBuffer Output; + + +uint getNextPowerOfTwo(uint number) { + return 2 << firstbithigh(number - 1); +} + +uint getLaneWithFirstBitSet(bool condition) { + uint4 ballot = WaveActiveBallot(condition); + if (all(ballot == 0)) { + return WaveGetLaneCount(); + } + return nbl::hlsl::glsl::subgroupBallotFindLSB(ballot); +} + +// findValue must be the same across the entire wave +// Could use something like WaveReadFirstLane to be fully sure +uint binarySearchLowerBoundFindValue(uint findValue, StructuredBuffer searchBuffer, uint searchBufferSize) { + uint lane = WaveGetLaneIndex(); + + uint left = 0; + uint right = searchBufferSize - 1; + + uint32_t range = getNextPowerOfTwo(right - left); + // do pivots as long as we can't coalesced load + while (range > WaveGetLaneCount()) + { + // there must be at least 1 gap between subsequent pivots + const uint32_t step = range / WaveGetLaneCount(); + const uint32_t halfStep = step >> 1; + const uint32_t pivotOffset = lane * step+halfStep; + const uint32_t pivotIndex = left + pivotOffset; + + uint4 notGreaterPivots = WaveActiveBallot(pivotIndex < right && !(findValue < searchBuffer[pivotIndex])); + uint partition = nbl::hlsl::glsl::subgroupBallotBitCount(notGreaterPivots); + // only move left if needed + if (partition != 0) + left += partition * step - halfStep; + // if we go into final half partition, the range becomes less too + range = partition != WaveGetLaneCount() ? step : halfStep; + } + + uint threadSearchIndex = left + lane; + bool laneValid = threadSearchIndex < searchBufferSize; + uint histAtIndex = laneValid ? searchBuffer[threadSearchIndex] : -1; + uint firstLaneGreaterThan = getLaneWithFirstBitSet(histAtIndex > findValue); + + return left + firstLaneGreaterThan - 1; +} + +static const uint32_t GroupsharedSize = WorkgroupSize; +groupshared uint shared_groupSearchBufferMinIndex; +groupshared uint shared_groupSearchBufferMaxIndex; +groupshared uint shared_groupSearchValues[WorkgroupSize]; + +// Binary search using the entire workgroup, making it log32 or log64 (every iteration, the possible set of +// values is divided by the number of lanes in a wave) +uint binarySearchLowerBoundCooperative(uint groupIndex, uint groupThread, StructuredBuffer searchBuffer, uint searchBufferSize) { + uint minSearchValue = groupIndex.x * GroupsharedSize; + uint maxSearchValue = ((groupIndex.x + 1) * GroupsharedSize) - 1; + + // On each workgroup, two subgroups do the search + // - One searches for the minimum, the other searches for the maximum + // - Store the minimum and maximum on groupshared memory, then do a barrier + uint wave = groupThread / WaveGetLaneCount(); + if (wave < 2) { + uint search = wave == 0 ? minSearchValue : maxSearchValue; + uint searchResult = binarySearchLowerBoundFindValue(search, searchBuffer, searchBufferSize); + if (WaveIsFirstLane()) { + if (wave == 0) shared_groupSearchBufferMinIndex = searchResult; + else shared_groupSearchBufferMaxIndex = searchResult; + } + } + GroupMemoryBarrierWithGroupSync(); + + // Since every instance has at least one triangle, we know that having workgroup values + // for each value in the range of minimum to maximum will suffice. + + // Write every value in the range to groupshared memory and barrier. + uint idx = shared_groupSearchBufferMinIndex + groupThread.x; + if (idx <= shared_groupSearchBufferMaxIndex) { + shared_groupSearchValues[groupThread.x] = searchBuffer[idx]; + } + GroupMemoryBarrierWithGroupSync(); + + uint maxValueIndex = shared_groupSearchBufferMaxIndex - shared_groupSearchBufferMinIndex; + + uint searchValue = minSearchValue + groupThread; + uint currentSearchValueIndex = 0; + uint laneValue = shared_groupSearchBufferMaxIndex; + while (currentSearchValueIndex <= maxValueIndex) { + uint curValue = shared_groupSearchValues[currentSearchValueIndex]; + if (curValue > searchValue) { + laneValue = shared_groupSearchBufferMinIndex + currentSearchValueIndex - 1; + break; + } + currentSearchValueIndex ++; + } + + return laneValue; +} + +[numthreads(WorkgroupSize,1,1)] +void main(const uint3 thread : SV_DispatchThreadID, const uint3 groupThread : SV_GroupThreadID, const uint3 group : SV_GroupID) +{ + Output[thread.x] = binarySearchLowerBoundCooperative(group.x, groupThread.x, Histogram, Constants.EntityCount); +} \ No newline at end of file diff --git a/72_CooperativeBinarySearch/app_resources/common.h b/72_CooperativeBinarySearch/app_resources/common.h new file mode 100644 index 000000000..65f606b08 --- /dev/null +++ b/72_CooperativeBinarySearch/app_resources/common.h @@ -0,0 +1,15 @@ +#ifndef _COOPERATIVE_BINARY_SEARCH_H_INCLUDED_ +#define _COOPERATIVE_BINARY_SEARCH_H_INCLUDED_ + +#include +#include + +// TODO: NBL_CONSTEXPR_NSPC_VAR +static const uint32_t WorkgroupSize = 256; + +struct PushConstants +{ + uint32_t EntityCount; +}; + +#endif // _COOPERATIVE_BINARY_SEARCH_H_INCLUDED_ diff --git a/72_CooperativeBinarySearch/config.json.template b/72_CooperativeBinarySearch/config.json.template new file mode 100644 index 000000000..24adf54fb --- /dev/null +++ b/72_CooperativeBinarySearch/config.json.template @@ -0,0 +1,28 @@ +{ + "enableParallelBuild": true, + "threadsPerBuildProcess" : 2, + "isExecuted": false, + "scriptPath": "", + "cmake": { + "configurations": [ "Release", "Debug", "RelWithDebInfo" ], + "buildModes": [], + "requiredOptions": [] + }, + "profiles": [ + { + "backend": "vulkan", + "platform": "windows", + "buildModes": [], + "runConfiguration": "Release", + "gpuArchitectures": [] + } + ], + "dependencies": [], + "data": [ + { + "dependencies": [], + "command": [""], + "outputs": [] + } + ] +} diff --git a/72_CooperativeBinarySearch/include/nbl/this_example/common.hpp b/72_CooperativeBinarySearch/include/nbl/this_example/common.hpp new file mode 100644 index 000000000..3745ca512 --- /dev/null +++ b/72_CooperativeBinarySearch/include/nbl/this_example/common.hpp @@ -0,0 +1,11 @@ +#ifndef _NBL_THIS_EXAMPLE_COMMON_H_INCLUDED_ +#define _NBL_THIS_EXAMPLE_COMMON_H_INCLUDED_ + +#include "nbl/examples/examples.hpp" + +// example's own headers +#include "nbl/ui/ICursorControl.h" // TODO: why not in nabla.h ? +#include "nbl/ext/ImGui/ImGui.h" +#include "imgui/imgui_internal.h" + +#endif // _NBL_THIS_EXAMPLE_COMMON_H_INCLUDED_ \ No newline at end of file diff --git a/72_CooperativeBinarySearch/main.cpp b/72_CooperativeBinarySearch/main.cpp new file mode 100644 index 000000000..81724c1b8 --- /dev/null +++ b/72_CooperativeBinarySearch/main.cpp @@ -0,0 +1,266 @@ +// Copyright (C) 2018-2024 - 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/examples/examples.hpp" +#include "nbl/system/IApplicationFramework.h" +#include "app_resources/common.h" + +#include +#include +#include + + +using namespace nbl; +using namespace nbl::core; +using namespace nbl::hlsl; +using namespace nbl::system; +using namespace nbl::asset; +using namespace nbl::ui; +using namespace nbl::video; +using namespace nbl::examples; + +// +constexpr uint32_t TestCaseIndices[] = { +#include "testCaseData.h" +}; +constexpr uint32_t numIndices = sizeof(TestCaseIndices) / sizeof(TestCaseIndices[0]); +constexpr uint32_t lastValue = TestCaseIndices[numIndices - 1]; +// just some extra stuff over the edge +constexpr uint32_t totalValues = lastValue + 100; + + +void cpu_tests(); + +class CooperativeBinarySearch final : public application_templates::MonoDeviceApplication, public BuiltinResourcesApplication +{ + using device_base_t = application_templates::MonoDeviceApplication; + using asset_base_t = BuiltinResourcesApplication; +public: + CooperativeBinarySearch(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) : + IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD) {} + + bool onAppInitialized(smart_refctd_ptr&& system) override + { + // Remember to call the base class initialization! + if (!device_base_t::onAppInitialized(smart_refctd_ptr(system))) + return false; + if (!asset_base_t::onAppInitialized(std::move(system))) + return false; + + m_queue = m_device->getQueue(0, 0); + m_commandPool = m_device->createCommandPool(m_queue->getFamilyIndex(), IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT); + m_commandPool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY, { &m_cmdbuf,1 }, smart_refctd_ptr(m_logger)); + + smart_refctd_ptr shader; + { + IAssetLoader::SAssetLoadParams lp = {}; + lp.logger = m_logger.get(); + lp.workingDirectory = ""; // virtual root + auto assetBundle = m_assetMgr->getAsset("app_resources/binarySearch.comp.hlsl", lp); + const auto assets = assetBundle.getContents(); + if (assets.empty()) + return logFail("Could not load shader!"); + + auto source = IAsset::castDown(assets[0]); + // The down-cast should not fail! + assert(source); + + // this time we skip the use of the asset converter since the ICPUShader->IGPUShader path is quick and simple + shader = m_device->compileShader({ source.get() }); + if (!shader) + return logFail("Creation of a GPU Shader to from CPU Shader source failed!"); + } + + const uint32_t bindingCount = 2u; + IGPUDescriptorSetLayout::SBinding bindings[bindingCount] = {}; + bindings[0].type = IDescriptor::E_TYPE::ET_STORAGE_BUFFER; // [[vk::binding(0)]] StructuredBuffer Histogram; + bindings[1].type = IDescriptor::E_TYPE::ET_STORAGE_BUFFER; // [[vk::binding(1)]] RWStructuredBuffer Output; + + for(int i = 0; i < bindingCount; ++i) + { + bindings[i].stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE; + bindings[i].count = 1; + bindings[i].binding = i; + } + m_descriptorSetLayout = m_device->createDescriptorSetLayout(bindings); + { + SPushConstantRange pcRange = {}; + pcRange.stageFlags = IShader::E_SHADER_STAGE::ESS_COMPUTE; + pcRange.offset = 0u; + pcRange.size = sizeof(PushConstants); + auto layout = m_device->createPipelineLayout({ &pcRange,1 }, smart_refctd_ptr(m_descriptorSetLayout)); + IGPUComputePipeline::SCreationParams params = {}; + params.layout = layout.get(); + params.shader.shader = shader.get(); + params.shader.entryPoint = "main"; + if (!m_device->createComputePipelines(nullptr, { ¶ms,1 }, &m_pipeline)) + return logFail("Failed to create compute pipeline!\n"); + } + + const size_t sizes[2] = {sizeof(TestCaseIndices),sizeof(uint32_t)*totalValues}; + for (uint32_t i = 0; i < bindingCount; i++) + { + m_buffers[i] = m_device->createBuffer(IGPUBuffer::SCreationParams { + {.size = sizes[i], .usage = + IGPUBuffer::E_USAGE_FLAGS::EUF_TRANSFER_DST_BIT | IGPUBuffer::E_USAGE_FLAGS::EUF_TRANSFER_SRC_BIT | + IGPUBuffer::E_USAGE_FLAGS::EUF_STORAGE_BUFFER_BIT, + } + }); + + auto reqs = m_buffers[i]->getMemoryReqs(); + reqs.memoryTypeBits &= m_device->getPhysicalDevice()->getHostVisibleMemoryTypeBits(); + + m_allocations[i] = m_device->allocate(reqs, m_buffers[i].get()); + + auto allocationType = i == 0 ? IDeviceMemoryAllocation::EMCAF_WRITE : IDeviceMemoryAllocation::EMCAF_READ; + auto mapResult = m_allocations[i].memory->map({ 0ull,m_allocations[i].memory->getAllocationSize() }, allocationType); + assert(mapResult); + } + + smart_refctd_ptr descriptorPool = nullptr; + { + IDescriptorPool::SCreateInfo createInfo = {}; + createInfo.maxSets = 1; + createInfo.maxDescriptorCount[static_cast(IDescriptor::E_TYPE::ET_STORAGE_BUFFER)] = bindingCount; + descriptorPool = m_device->createDescriptorPool(std::move(createInfo)); + } + + m_descriptorSet = descriptorPool->createDescriptorSet(smart_refctd_ptr(m_descriptorSetLayout)); + + IGPUDescriptorSet::SDescriptorInfo descriptorInfos[bindingCount] = {}; + IGPUDescriptorSet::SWriteDescriptorSet writeDescriptorSets[bindingCount] = {}; + + for(int i = 0; i < bindingCount; ++i) + { + writeDescriptorSets[i].info = &descriptorInfos[i]; + writeDescriptorSets[i].dstSet = m_descriptorSet.get(); + writeDescriptorSets[i].binding = i; + writeDescriptorSets[i].count = bindings[i].count; + + descriptorInfos[i].desc = m_buffers[i]; + descriptorInfos[i].info.buffer.size = ~0ull; + } + + m_device->updateDescriptorSets(bindingCount, writeDescriptorSets, 0u, nullptr); + + // Write test data to the m_buffers[0] + auto outPtr = m_allocations[0].memory->getMappedPointer(); + assert(outPtr); + memcpy( + reinterpret_cast(outPtr), + reinterpret_cast(&TestCaseIndices[0]), + sizeof(TestCaseIndices) + ); + + // In contrast to fences, we just need one semaphore to rule all dispatches + return true; + } + + void onAppTerminated_impl() override + { + m_device->waitIdle(); + } + + void workLoopBody() override + { + cpu_tests(); + + constexpr auto StartedValue = 0; + + smart_refctd_ptr progress = m_device->createSemaphore(StartedValue); + + m_cmdbuf->reset(IGPUCommandBuffer::RESET_FLAGS::RELEASE_RESOURCES_BIT); + m_cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT); + + + IGPUCommandBuffer::SPipelineBarrierDependencyInfo::buffer_barrier_t layoutBufferBarrier[1] = { { + .barrier = { + .dep = { + .srcStageMask = PIPELINE_STAGE_FLAGS::HOST_BIT, + .srcAccessMask = ACCESS_FLAGS::HOST_WRITE_BIT, + .dstStageMask = PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT, + .dstAccessMask = ACCESS_FLAGS::SHADER_WRITE_BITS + } + }, + // whole buffer because we transferred the contents into it + .range = {.offset = 0,.size = m_buffers[1]->getCreationParams().size,.buffer = m_buffers[1]} + } }; + + const IGPUCommandBuffer::SPipelineBarrierDependencyInfo depInfo = { .bufBarriers = layoutBufferBarrier }; + m_cmdbuf->pipelineBarrier(EDF_NONE, depInfo); + + + const IGPUDescriptorSet* set = m_descriptorSet.get(); + PushConstants coopBinarySearchPC = { + .EntityCount = numIndices, + }; + + m_cmdbuf->bindComputePipeline(m_pipeline.get()); + m_cmdbuf->bindDescriptorSets(EPBP_COMPUTE, m_pipeline->getLayout(), 0u, 1u, &set); + m_cmdbuf->pushConstants(m_pipeline->getLayout(), nbl::hlsl::ShaderStage::ESS_COMPUTE, 0u, sizeof(PushConstants), &coopBinarySearchPC); + m_cmdbuf->dispatch((totalValues + 255u) / 256u, 1u, 1u); + + layoutBufferBarrier[0].barrier.dep = layoutBufferBarrier[0].barrier.dep.nextBarrier(PIPELINE_STAGE_FLAGS::COPY_BIT,ACCESS_FLAGS::TRANSFER_READ_BIT); + m_cmdbuf->pipelineBarrier(EDF_NONE,depInfo); + + m_cmdbuf->end(); + + { + constexpr auto FinishedValue = 69; + IQueue::SSubmitInfo submitInfos[1] = {}; + const IQueue::SSubmitInfo::SCommandBufferInfo cmdbufs[] = { {.cmdbuf = m_cmdbuf.get()} }; + submitInfos[0].commandBuffers = cmdbufs; + const IQueue::SSubmitInfo::SSemaphoreInfo signals[] = { {.semaphore = progress.get(),.value = FinishedValue,.stageMask = PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT} }; + submitInfos[0].signalSemaphores = signals; + m_api->startCapture(); + m_queue->submit(submitInfos); + m_api->endCapture(); + const ISemaphore::SWaitInfo waitInfos[] = { { + .semaphore = progress.get(), + .value = FinishedValue + } }; + m_device->blockForSemaphores(waitInfos); + } + + auto ptr = m_allocations[1].memory->getMappedPointer(); + assert(ptr); + + uint32_t* valuesPtr = reinterpret_cast(ptr); + for (uint32_t i = 0; i < totalValues; i++) { + uint32_t value = valuesPtr[i]; + const uint32_t* binarySearchResult = std::upper_bound(TestCaseIndices, TestCaseIndices + numIndices, i); + uint32_t lowerBoundIndex = std::distance(TestCaseIndices, binarySearchResult) - 1; + assert(value == lowerBoundIndex); + } + + m_keepRunning = false; + } + + bool keepRunning() override + { + return m_keepRunning; + } + + +private: + smart_refctd_ptr m_pipeline = nullptr; + smart_refctd_ptr m_descriptorSetLayout; + smart_refctd_ptr m_descriptorSet; + + smart_refctd_ptr m_buffers[2]; + nbl::video::IDeviceMemoryAllocator::SAllocation m_allocations[2] = {}; + smart_refctd_ptr m_cmdbuf = nullptr; + IQueue* m_queue; + smart_refctd_ptr m_commandPool; + uint64_t m_iteration = 0; + constexpr static inline uint64_t MaxIterations = 200; + + bool m_keepRunning = true; +}; + +NBL_MAIN_FUNC(CooperativeBinarySearch) + +void cpu_tests() +{ +} diff --git a/72_CooperativeBinarySearch/testCaseData.h b/72_CooperativeBinarySearch/testCaseData.h new file mode 100644 index 000000000..16153780e --- /dev/null +++ b/72_CooperativeBinarySearch/testCaseData.h @@ -0,0 +1,1192 @@ +0, +298, +554, +582, +912, +1074, +1076, +1078, +1170, +1188, +2140, +2414, +2736, +2738, +3980, +4800, +5898, +5900, +6936, +8106, +8152, +8650, +8844, +8930, +9504, +10244, +10826, +10828, +11126, +11430, +12206, +13764, +14010, +15302, +15624, +15656, +16414, +16494, +17368, +17432, +18312, +18948, +19376, +19818, +20146, +20604, +21240, +22446, +23482, +24914, +25042, +25538, +26764, +27564, +27566, +28472, +29450, +30202, +31474, +32160, +32676, +33792, +33794, +34704, +36540, +37456, +37950, +38364, +39274, +40442, +40518, +41412, +41590, +41950, +42022, +42714, +43464, +43790, +43792, +44876, +44878, +46188, +46572, +47352, +47650, +48242, +49856, +49858, +50506, +50968, +50970, +51152, +51154, +52870, +52884, +53332, +53334, +53904, +53964, +53966, +53968, +53970, +53972, +53974, +53976, +53978, +53980, +54514, +54516, +54518, +54520, +54762, +55866, +56462, +56478, +56480, +56482, +57510, +57568, +57570, +57572, +57846, +57848, +58760, +59408, +59438, +60198, +60200, +60202, +60204, +60284, +60938, +61274, +61720, +62296, +63116, +63378, +63380, +63382, +63384, +63386, +63388, +63904, +64572, +65142, +65144, +65146, +65554, +65738, +66052, +67016, +67424, +67566, +68270, +68272, +68610, +69240, +69870, +70988, +72622, +73258, +73260, +73580, +74524, +74880, +74958, +74960, +74962, +75114, +75116, +75622, +77144, +77798, +77800, +78314, +79566, +79568, +79570, +79572, +79850, +79852, +81576, +81684, +81686, +82492, +82494, +82496, +82498, +83990, +84860, +84988, +84990, +85138, +85772, +86120, +86122, +86564, +87402, +87404, +87602, +88676, +88714, +88780, +89560, +89732, +90786, +91128, +91130, +91272, +91522, +91804, +92588, +92590, +92834, +93268, +93736, +94448, +94704, +94706, +95074, +95076, +96706, +97040, +97770, +98000, +98676, +99968, +100074, +100318, +100602, +100914, +101020, +101872, +101878, +103078, +104246, +104266, +105436, +106332, +106954, +107856, +108954, +110320, +110780, +111588, +111882, +112502, +112676, +113496, +114070, +115204, +115422, +115424, +115858, +116420, +117426, +118504, +118870, +119296, +119618, +119650, +120408, +120488, +121362, +121426, +122306, +122942, +123370, +123812, +124140, +124598, +125234, +126440, +127476, +128908, +129036, +129532, +130758, +131558, +131560, +132466, +133444, +134196, +135468, +136154, +136670, +137786, +137788, +138698, +140534, +140832, +141608, +142422, +143220, +143468, +143714, +144504, +145078, +145670, +146224, +146874, +147726, +148692, +149536, +151032, +151126, +153382, +154128, +155190, +155212, +156324, +156484, +156526, +157026, +158242, +158446, +158448, +158594, +159256, +160350, +160444, +161040, +161624, +162418, +162524, +162768, +163052, +163364, +163470, +164322, +164328, +165528, +166696, +166716, +167886, +168782, +169404, +170306, +171404, +172770, +173230, +174038, +174332, +174952, +175126, +175946, +176520, +177654, +177872, +177874, +178308, +178870, +179876, +180954, +181320, +181746, +182160, +183070, +184238, +184314, +185208, +185386, +185746, +185818, +186510, +187260, +187586, +187588, +188672, +188674, +189984, +190368, +191148, +191446, +192038, +193652, +193654, +194302, +194764, +194766, +194948, +194950, +196666, +196680, +197128, +197130, +197700, +198048, +198824, +199638, +200436, +200684, +200930, +201720, +202294, +202886, +203440, +204090, +204942, +205908, +206752, +208248, +208342, +210598, +211344, +212406, +212428, +213540, +213700, +213742, +214242, +215458, +215662, +215664, +215810, +216472, +217566, +217660, +218256, +218316, +218318, +218320, +218322, +218324, +218326, +218328, +218330, +218332, +218866, +218868, +218870, +218872, +219114, +220218, +220814, +220830, +220832, +220834, +221862, +221920, +221922, +221924, +222198, +222200, +223112, +223760, +223790, +224550, +224552, +224554, +224556, +225140, +225794, +226130, +226576, +227152, +227972, +228234, +228236, +228238, +228240, +228242, +228244, +228760, +229428, +229998, +230000, +230002, +230410, +230594, +230908, +231872, +232280, +232422, +233126, +233128, +233466, +234096, +234726, +235844, +237478, +238114, +238116, +238512, +239256, +239812, +240660, +241950, +243244, +243366, +244346, +244412, +244710, +245202, +246504, +246728, +246988, +247592, +248630, +249562, +250962, +251964, +252562, +253140, +253412, +254672, +255276, +256084, +256160, +256378, +257104, +257602, +257776, +258240, +258556, +258614, +259208, +260496, +261202, +261398, +262284, +262610, +262976, +263578, +264622, +265558, +266692, +266756, +268110, +268994, +269158, +269718, +270388, +270768, +271098, +271786, +272398, +272996, +273140, +273612, +274226, +274660, +275070, +275416, +275634, +275680, +276088, +276408, +276410, +276852, +277690, +277692, +277890, +278964, +279002, +279068, +279848, +280020, +281074, +281416, +281418, +281560, +281810, +282092, +282876, +282878, +283122, +283556, +284024, +284736, +284992, +284994, +285362, +285364, +286994, +287328, +288058, +288288, +288964, +289708, +289746, +290266, +291136, +292152, +292740, +292834, +293708, +293768, +293936, +294846, +295028, +295040, +295130, +295372, +296154, +296736, +297250, +297606, +298068, +298310, +299420, +300362, +301176, +301502, +301878, +302702, +303576, +303896, +305170, +305928, +306070, +306150, +307094, +307450, +307528, +307530, +307532, +307684, +307686, +308192, +309714, +310368, +310370, +310884, +312136, +312138, +312140, +312142, +312420, +312422, +314146, +314254, +314256, +315062, +315064, +315066, +315068, +316560, +317430, +317558, +317560, +317708, +318342, +319182, +319992, +320612, +320956, +321068, +321076, +322784, +322914, +323106, +324036, +324708, +326092, +326994, +327332, +328080, +328444, +329022, +329256, +330454, +331304, +331610, +332432, +332440, +333298, +334300, +334478, +334622, +335370, +335818, +336456, +336618, +337930, +338932, +339158, +339258, +339746, +340226, +340254, +340256, +340988, +341638, +342674, +343168, +343440, +344024, +344026, +344106, +345118, +346124, +347350, +348560, +348878, +349066, +350192, +350840, +351388, +353610, +354562, +355208, +356084, +356966, +358222, +359304, +359470, +360054, +360710, +360920, +361896, +362930, +362962, +363128, +363234, +363272, +363284, +363456, +363732, +364418, +364926, +365096, +365170, +365920, +366796, +367838, +368232, +368940, +369508, +369530, +370886, +371156, +371348, +372384, +372680, +372690, +373252, +373676, +374168, +374424, +374452, +374782, +374944, +374946, +374948, +375040, +375058, +376010, +376284, +376606, +376608, +377850, +378670, +379768, +379770, +380806, +381976, +382022, +382520, +382714, +382800, +383374, +384114, +384696, +384698, +384996, +385300, +386076, +387634, +387880, +388796, +389290, +389302, +389314, +389338, +389406, +389434, +389470, +389840, +389952, +390908, +391076, +391188, +392118, +392458, +392472, +392622, +392766, +393448, +394586, +394816, +394824, +395486, +396218, +396880, +396910, +397066, +397076, +397124, +397678, +398050, +399160, +400080, +401696, +401762, +402400, +402500, +402512, +403152, +404038, +404444, +404648, +404740, +405322, +406252, +407076, +408252, +408634, +409354, +410112, +411138, +411672, +411880, +412232, +412926, +412956, +413864, +414624, +415770, +415978, +417234, +417256, +417264, +418562, +418812, +418824, +418836, +418860, +418928, +418956, +418992, +419362, +419474, +420430, +420598, +420710, +421640, +421980, +421994, +422144, +422288, +422970, +424108, +424338, +424346, +425008, +425740, +426402, +426432, +426588, +426598, +426646, +427200, +427572, +428682, +429602, +430346, +430412, +431050, +431150, +431162, +431802, +432688, +433094, +433298, +433390, +433972, +434902, +435726, +436902, +437284, +438004, +438762, +439788, +440322, +440530, +440882, +441576, +441606, +442514, +443274, +444420, +444628, +445884, +445906, +445914, +447212, +447462, +448464, +448690, +448790, +449278, +449758, +449786, +449788, +450520, +451170, +452206, +452700, +452972, +453556, +453558, +453638, +454650, +455656, +456882, +458092, +458410, +458598, +459724, +460372, +460920, +463142, +464094, +464740, +465616, +466498, +467754, +468836, +469002, +469586, +470180, +471468, +472174, +472370, +473256, +473582, +473948, +474550, +475594, +476530, +477664, +477728, +479082, +479966, +480130, +480690, +481360, +481740, +482070, +482758, +483370, +483968, +484112, +484584, +485198, +485632, +486042, +486388, +486606, +486652, +487060, +488676, +489420, +489976, +490824, +492114, +493408, +493530, +494510, +494576, +494874, +495366, +496668, +496892, +497152, +497756, +498794, +499726, +501126, +502128, +502726, +503304, +503576, +504836, +505440, +506248, +506324, +506542, +507268, +507766, +507940, +508404, +508720, +509514, +510170, +510380, +511356, +512390, +512422, +512588, +512694, +512732, +512744, +512916, +513192, +513878, +514386, +514556, +514630, +515380, +516256, +517298, +517692, +518400, +518968, +518990, +520346, +520616, +520808, +521844, +522140, +522150, +522712, +523136, +523628, +524468, +525278, +525898, +526242, +526354, +526362, +528070, +528200, +528392, +529322, +529994, +531378, +532280, +532618, +533366, +533730, +534308, +534542, +535740, +536590, +536896, +537718, +537726, +538584, +539586, +539764, +539908, +540656, +541104, +541742, +541904, +543216, +543612, +543650, +544170, +545040, +546056, +546644, +546738, +547612, +547672, +547840, +548750, +548932, +548944, +549034, +549276, +550058, +550640, +551154, +551510, +551972, +552214, +553324, +554266, +555080, +555406, +555782, +556606, +557480, +557800, +559074, +559832, +559974, +550468, +551276, +552568, +552866, +553798, +554120, +554294, +555554, +556448, +556874, +557328, +557680, +558532, +559844, +560774, +561050, +561458, +562684, +563910, +564026, +564542, +565294, +565434, +566278, +567580, +568006, +568328, +569626, +570350, +570998, +572812, +573008, +573500, +573828, +573840, +573842, +574798, +576066, +576774, +577182, +577184, +577522, +577524, +578734, +579854, +579856, +581128, +581278, +582296, +583496, +583944, +584160, +584844, +584954, +584968, +585486, +586592, +586594, +587158, +587320, +588006, +589012, +590302, +590366, +590444, +590944, +581786, +582234, +582920, +582922, +564780, +565486, +565684, +566570, +566896, +567262, +567864, +568958, +570268, +570844, +572014, +573368, +574252, +574416, +574976, +575646, +576026, +576356, +577044, +577046, +577644, +577788, +578260, +578874, +579308, +579718, +580288, +580942, +581534, +581536, +576350, +576352 \ No newline at end of file diff --git a/73_SolidAngleVisualizer/CMakeLists.txt b/73_SolidAngleVisualizer/CMakeLists.txt new file mode 100644 index 000000000..5d0021f61 --- /dev/null +++ b/73_SolidAngleVisualizer/CMakeLists.txt @@ -0,0 +1,20 @@ +if(NBL_BUILD_IMGUI) + set(NBL_EXTRA_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/src/transform.cpp" + ) + + set(NBL_INCLUDE_SERACH_DIRECTORIES + "${CMAKE_CURRENT_SOURCE_DIR}/include" + ) + + list(APPEND NBL_LIBRARIES + imtestengine + imguizmo + "${NBL_EXT_IMGUI_UI_LIB}" + ) + + # TODO; Arek I removed `NBL_EXECUTABLE_PROJECT_CREATION_PCH_TARGET` from the last parameter here, doesn't this macro have 4 arguments anyway !? + nbl_create_executable_project("${NBL_EXTRA_SOURCES}" "" "${NBL_INCLUDE_SERACH_DIRECTORIES}" "${NBL_LIBRARIES}") + # TODO: Arek temporarily disabled cause I haven't figured out how to make this target yet + # LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} nblExamplesGeometrySpirvBRD) +endif() \ No newline at end of file diff --git a/73_SolidAngleVisualizer/README.md b/73_SolidAngleVisualizer/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/73_SolidAngleVisualizer/app_resources/hlsl/Drawing.hlsl b/73_SolidAngleVisualizer/app_resources/hlsl/Drawing.hlsl new file mode 100644 index 000000000..89dfd4ae6 --- /dev/null +++ b/73_SolidAngleVisualizer/app_resources/hlsl/Drawing.hlsl @@ -0,0 +1,411 @@ +#ifndef _DEBUG_HLSL_ +#define _DEBUG_HLSL_ +#include "common.hlsl" + +float2 sphereToCircle(float3 spherePoint) +{ + if (spherePoint.z >= 0.0f) + { + return spherePoint.xy * CIRCLE_RADIUS; + } + else + { + float r2 = (1.0f - spherePoint.z) / (1.0f + spherePoint.z); + float uv2Plus1 = r2 + 1.0f; + return (spherePoint.xy * uv2Plus1 / 2.0f) * CIRCLE_RADIUS; + } +} + +float drawGreatCircleArc(float3 fragPos, float3 points[2], float aaWidth, float width = 0.01f) +{ + float3 v0 = normalize(points[0]); + float3 v1 = normalize(points[1]); + float3 p = normalize(fragPos); + + float3 arcNormal = normalize(cross(v0, v1)); + float dist = abs(dot(p, arcNormal)); + + float dotMid = dot(v0, v1); + bool onArc = (dot(p, v0) >= dotMid) && (dot(p, v1) >= dotMid); + + if (!onArc) + return 0.0f; + + float avgDepth = (length(points[0]) + length(points[1])) * 0.5f; + float depthScale = 3.0f / avgDepth; + + width = min(width * depthScale, 0.02f); + float alpha = 1.0f - smoothstep(width - aaWidth, width + aaWidth, dist); + + return alpha; +} + +float drawCross2D(float2 fragPos, float2 center, float size, float thickness) +{ + float2 p = abs(fragPos - center); + + // Check if point is inside the cross (horizontal or vertical bar) + bool inHorizontal = (p.x <= size && p.y <= thickness); + bool inVertical = (p.y <= size && p.x <= thickness); + + return (inHorizontal || inVertical) ? 1.0f : 0.0f; +} + +float4 drawHiddenEdges(float3 spherePos, uint32_t silEdgeMask, float aaWidth) +{ + float4 color = 0; + float3 hiddenEdgeColor = float3(0.1, 0.1, 0.1); + + NBL_UNROLL + for (int32_t i = 0; i < 12; i++) + { + // skip silhouette edges + if (silEdgeMask & (1u << i)) + continue; + + int2 edge = allEdges[i]; + + float3 v0 = normalize(getVertex(edge.x)); + float3 v1 = normalize(getVertex(edge.y)); + + bool neg0 = v0.z < 0.0f; + bool neg1 = v1.z < 0.0f; + + // fully hidden + if (neg0 && neg1) + continue; + + float3 p0 = v0; + float3 p1 = v1; + + // clip if needed + if (neg0 ^ neg1) + { + float t = v0.z / (v0.z - v1.z); + float3 clip = normalize(lerp(v0, v1, t)); + + p0 = neg0 ? clip : v0; + p1 = neg1 ? clip : v1; + } + + float3 pts[2] = {p0, p1}; + float4 c = drawGreatCircleArc(spherePos, pts, aaWidth, 0.005f); + color += float4(hiddenEdgeColor * c.a, c.a); + } + + return color; +} + +float4 drawCorners(float2 p, float aaWidth) +{ + float4 color = 0; + + float dotSize = 0.02f; + float innerDotSize = dotSize * 0.5f; + + for (int32_t i = 0; i < 8; i++) + { + float3 corner3D = normalize(getVertex(i)); + float2 cornerPos = sphereToCircle(corner3D); + + float dist = length(p - cornerPos); + + // outer dot + float outerAlpha = 1.0f - smoothstep(dotSize - aaWidth, + dotSize + aaWidth, + dist); + + if (outerAlpha <= 0.0f) + continue; + + float3 dotColor = colorLUT[i]; + color += float4(dotColor * outerAlpha, outerAlpha); + + // ------------------------------------------------- + // inner black dot for hidden corners + // ------------------------------------------------- + if (corner3D.z < 0.0f) + { + float innerAlpha = 1.0f - smoothstep(innerDotSize - aaWidth, + innerDotSize + aaWidth, + dist); + + // ensure it stays inside the outer dot + innerAlpha *= outerAlpha; + + float3 innerColor = float3(0.0, 0.0, 0.0); + color -= float4(innerAlpha.xxx, 0.0f); + } + } + + return color; +} + +float4 drawClippedSilhouetteVertices(float2 p, ClippedSilhouette silhouette, float aaWidth) +{ + float4 color = 0; + float dotSize = 0.03f; + + for (uint i = 0; i < silhouette.count; i++) + { + float3 corner3D = normalize(silhouette.vertices[i]); + float2 cornerPos = sphereToCircle(corner3D); + + float dist = length(p - cornerPos); + + // Smooth circle for the vertex + float alpha = 1.0f - smoothstep(dotSize * 0.8f, dotSize, dist); + + if (alpha > 0.0f) + { + // Color gradient: Red (index 0) to Cyan (last index) + // This helps verify the CCW winding order visually + float t = float(i) / float(max(1u, silhouette.count - 1)); + float3 vertexColor = lerp(float3(1, 0, 0), float3(0, 1, 1), t); + + color += float4(vertexColor * alpha, alpha); + } + } + return color; +} + +float4 drawRing(float2 p, float aaWidth) +{ + float positionLength = length(p); + float ringWidth = 0.003f; + float ringDistance = abs(positionLength - CIRCLE_RADIUS); + float ringAlpha = 1.0f - smoothstep(ringWidth - aaWidth, ringWidth + aaWidth, ringDistance); + return ringAlpha * float4(1, 1, 1, 1); +} + +// Returns the number of visible faces and populates the faceIndices array +uint getVisibleFaces(int3 region, out uint faceIndices[3]) +{ + uint count = 0; + + // Check X axis + if (region.x == 0) + faceIndices[count++] = 3; // X+ + else if (region.x == 2) + faceIndices[count++] = 2; // X- + + // Check Y axis + if (region.y == 0) + faceIndices[count++] = 5; // Y+ + else if (region.y == 2) + faceIndices[count++] = 4; // Y- + + // Check Z axis + if (region.z == 0) + faceIndices[count++] = 1; // Z+ + else if (region.z == 2) + faceIndices[count++] = 0; // Z- + + return count; +} + +float4 drawVisibleFaceOverlay(float3 spherePos, int3 region, float aaWidth) +{ + uint faceIndices[3]; + uint count = getVisibleFaces(region, faceIndices); + float4 color = 0; + + for (uint i = 0; i < count; i++) + { + uint fIdx = faceIndices[i]; + float3 n = localNormals[fIdx]; + + // Transform normal to world space (using the same logic as your corners) + float3 worldNormal = -normalize(mul((float3x3)pc.modelMatrix, n)); + worldNormal.z = -worldNormal.z; // Invert Z for correct orientation + + // Very basic visualization: highlight if the sphere position + // is generally pointing towards that face's normal + float alignment = dot(spherePos, worldNormal); + if (alignment > 0.95f) + { + // Use different colors for different face indices + color += float4(colorLUT[fIdx % 24], 0.5f); + } + } + return color; +} + +// Check if a face on the hemisphere is visible from camera at origin +bool isFaceVisible(float3 faceCenter, float3 faceNormal) +{ + float3 viewVec = normalize(-faceCenter); // Vector from camera to face + return dot(faceNormal, viewVec) > 0.0f; +} + +float4 drawFaces(float3 spherePos, float aaWidth) +{ + float4 color = 0.0f; + float3 p = normalize(spherePos); + + float3x3 rotMatrix = (float3x3)pc.modelMatrix; + + // Check each of the 6 faces + for (int32_t faceIdx = 0; faceIdx < 6; faceIdx++) + { + float3 n_world = mul(rotMatrix, localNormals[faceIdx]); + + // Check if face is visible + if (!isFaceVisible(faceCenters[faceIdx], n_world)) + continue; + + // Get the 4 corners of this face + float3 faceVerts[4]; + for (int32_t i = 0; i < 4; i++) + { + int32_t cornerIdx = faceToCorners[faceIdx][i]; + faceVerts[i] = normalize(getVertex(cornerIdx)); + } + + // Compute face center for winding + float3 faceCenter = float3(0, 0, 0); + for (int32_t i = 0; i < 4; i++) + faceCenter += faceVerts[i]; + faceCenter = normalize(faceCenter); + + // Check if point is inside this face + bool isInside = true; + float minDist = 1e10; + + for (int32_t i = 0; i < 4; i++) + { + float3 v0 = faceVerts[i]; + float3 v1 = faceVerts[(i + 1) % 4]; + + // Skip edges behind camera + if (v0.z < 0.0f && v1.z < 0.0f) + { + isInside = false; + break; + } + + // Great circle normal + float3 edgeNormal = normalize(cross(v0, v1)); + + // Ensure normal points inward + if (dot(edgeNormal, faceCenter) < 0.0f) + edgeNormal = -edgeNormal; + + float d = dot(p, edgeNormal); + + if (d < -1e-6f) + { + isInside = false; + break; + } + + minDist = min(minDist, abs(d)); + } + + if (isInside) + { + float alpha = smoothstep(0.0f, aaWidth * 2.0f, minDist); + + // Use colorLUT based on face index (0-5) + float3 faceColor = colorLUT[faceIdx]; + + float shading = saturate(p.z * 0.8f + 0.2f); + color += float4(faceColor * shading * alpha, alpha); + } + } + + return color; +} + +int32_t getEdgeVisibility(int32_t edgeIdx) +{ + + // Adjacency of edges to faces + // Corrected Adjacency of edges to faces + static const int2 edgeToFaces[12] = { + // Edge Index: | allEdges[i] | Shared Faces: + + /* 0 (0-1) */ {4, 0}, // Y- (4) and Z- (0) + /* 1 (2-3) */ {5, 0}, // Y+ (5) and Z- (0) + /* 2 (4-5) */ {4, 1}, // Y- (4) and Z+ (1) + /* 3 (6-7) */ {5, 1}, // Y+ (5) and Z+ (1) + + /* 4 (0-2) */ {2, 0}, // X- (2) and Z- (0) + /* 5 (1-3) */ {3, 0}, // X+ (3) and Z- (0) + /* 6 (4-6) */ {2, 1}, // X- (2) and Z+ (1) + /* 7 (5-7) */ {3, 1}, // X+ (3) and Z+ (1) + + /* 8 (0-4) */ {2, 4}, // X- (2) and Y- (4) + /* 9 (1-5) */ {3, 4}, // X+ (3) and Y- (4) + /* 10 (2-6) */ {2, 5}, // X- (2) and Y+ (5) + /* 11 (3-7) */ {3, 5} // X+ (3) and Y+ (5) + }; + + int2 faces = edgeToFaces[edgeIdx]; + + // Transform normals to world space + float3x3 rotMatrix = (float3x3)pc.modelMatrix; + float3 n_world_f1 = mul(rotMatrix, localNormals[faces.x]); + float3 n_world_f2 = mul(rotMatrix, localNormals[faces.y]); + + bool visible1 = isFaceVisible(faceCenters[faces.x], n_world_f1); + bool visible2 = isFaceVisible(faceCenters[faces.y], n_world_f2); + + // Silhouette: exactly one face visible + if (visible1 != visible2) + return 1; + + // Inner edge: both faces visible + if (visible1 && visible2) + return 2; + + // Hidden edge: both faces hidden + return 0; +} + +#if DEBUG_DATA +uint32_t computeGroundTruthEdgeMask() +{ + uint32_t mask = 0u; + NBL_UNROLL + for (int32_t j = 0; j < 12; j++) + { + // getEdgeVisibility returns 1 for a silhouette edge based on 3D geometry + if (getEdgeVisibility(j) == 1) + { + mask |= (1u << j); + } + } + return mask; +} + +void validateEdgeVisibility(uint32_t sil, int32_t vertexCount, uint32_t generatedSilMask) +{ + uint32_t mismatchAccumulator = 0; + + // The Ground Truth now represents the full 3D silhouette, clipped or not. + uint32_t groundTruthMask = computeGroundTruthEdgeMask(); + + // The comparison checks if the generated mask perfectly matches the full 3D ground truth. + uint32_t mismatchMask = groundTruthMask ^ generatedSilMask; + + if (mismatchMask != 0) + { + NBL_UNROLL + for (int32_t j = 0; j < 12; j++) + { + if ((mismatchMask >> j) & 1u) + { + int2 edge = allEdges[j]; + // Accumulate vertex indices where error occurred + mismatchAccumulator |= (1u << edge.x) | (1u << edge.y); + } + } + } + + // Simple Write (assuming all fragments calculate the same result) + InterlockedOr(DebugDataBuffer[0].edgeVisibilityMismatch, mismatchAccumulator); +} +#endif + +#endif // _DEBUG_HLSL_ diff --git a/73_SolidAngleVisualizer/app_resources/hlsl/Sampling.hlsl b/73_SolidAngleVisualizer/app_resources/hlsl/Sampling.hlsl new file mode 100644 index 000000000..d213d8b94 --- /dev/null +++ b/73_SolidAngleVisualizer/app_resources/hlsl/Sampling.hlsl @@ -0,0 +1,247 @@ +#ifndef _SAMPLING_HLSL_ +#define _SAMPLING_HLSL_ + +// Include the spherical triangle utilities +#include +#include +#include "nbl/builtin/hlsl/random/pcg.hlsl" +#include "nbl/builtin/hlsl/random/xoroshiro.hlsl" + +using namespace nbl::hlsl; +// Sampling mode enum +#define SAMPLING_MODE_SOLID_ANGLE 0 +#define SAMPLING_MODE_PROJECTED_SOLID_ANGLE 1 + +// Maximum number of triangles we can have after clipping +// Without clipping, max 3 faces can be visible at once +// With clipping, can be more. 7 - 2 = 5 max triangles because fanning from one vertex +#define MAX_TRIANGLES 5 + +struct SamplingData +{ + float32_t triangleWeights[MAX_TRIANGLES]; + uint32_t triangleIndices[MAX_TRIANGLES]; // Store the 'i' value for each valid triangle + uint32_t count; + float32_t totalWeight; +}; + +float32_t2 nextRandomUnorm2(inout nbl::hlsl::Xoroshiro64StarStar rnd) +{ + return float32_t2( + float32_t(rnd()) * 2.3283064365386963e-10, + float32_t(rnd()) * 2.3283064365386963e-10); +} + +float32_t computeProjectedSolidAngleFallback(float32_t3 v0, float32_t3 v1, float32_t3 v2, float32_t3 N) +{ + // 1. Get edge normals (unit vectors) + // We use the cross product of the vertices (unit vectors on sphere) + float32_t3 n0 = cross(v0, v1); + float32_t3 n1 = cross(v1, v2); + float32_t3 n2 = cross(v2, v0); + + // 2. Normalize edge normals (magnitude is sin of the arc length) + float32_t l0 = length(n0); + float32_t l1 = length(n1); + float32_t l2 = length(n2); + + // Guard against degenerate triangles + if (l0 < 1e-7 || l1 < 1e-7 || l2 < 1e-7) + return 0.0f; + + n0 /= l0; + n1 /= l1; + n2 /= l2; + + // 3. Get arc lengths (angles in radians) + float32_t a = asin(clamp(l0, -1.0, 1.0)); // side v0-v1 + float32_t b = asin(clamp(l1, -1.0, 1.0)); // side v1-v2 + float32_t c = asin(clamp(l2, -1.0, 1.0)); // side v2-v0 + + // Handle acos/asin quadrant if dot product is negative + if (dot(v0, v1) < 0) + a = 3.14159265 - a; + if (dot(v1, v2) < 0) + b = 3.14159265 - b; + if (dot(v2, v0) < 0) + c = 3.14159265 - c; + + // 4. Compute projected solid angle + float32_t Gamma = 0.5f * (a * dot(n0, N) + b * dot(n1, N) + c * dot(n2, N)); + + // Return the absolute value of the total (to handle CW/CCW triangles) + return abs(Gamma); +} + +// Build sampling data - store weights and vertex indices +SamplingData buildSamplingDataFromSilhouette(ClippedSilhouette silhouette, int32_t samplingMode) +{ + SamplingData data; + data.count = 0; + data.totalWeight = 0; + + if (silhouette.count < 3) + return data; + + float32_t3 v0 = silhouette.vertices[0]; + float32_t3 origin = float32_t3(0, 0, 0); + + for (uint32_t i = 1; i < silhouette.count - 1; i++) + { + float32_t3 v1 = silhouette.vertices[i]; + float32_t3 v2 = silhouette.vertices[i + 1]; + + shapes::SphericalTriangle shapeTri = shapes::SphericalTriangle::create(v0, v1, v2, origin); + + if (shapeTri.pyramidAngles()) + continue; + + float32_t weight; + if (samplingMode == SAMPLING_MODE_PROJECTED_SOLID_ANGLE) + { + float32_t3 faceNormal = normalize(cross(v1 - v0, v2 - v0)); // TODO: precompute? + weight = computeProjectedSolidAngleFallback(normalize(v0), normalize(v1), normalize(v2), faceNormal); + } + else + { + weight = shapeTri.solidAngleOfTriangle(); + } + + if (weight <= 0.0f) + continue; + + data.triangleWeights[data.count] = weight; + data.triangleIndices[data.count] = i; // Store the original vertex index, we need to account for skipped degenerate triangles. + data.totalWeight += weight; + data.count++; + } + +#ifdef DEBUG_DATA + // Assert no edge has both vertices antipodal (lune case) + for (uint32_t i = 0; i < silhouette.count; i++) + { + uint32_t j = (i + 1) % silhouette.count; + float32_t3 n1 = normalize(silhouette.vertices[i]); + float32_t3 n2 = normalize(silhouette.vertices[j]); + + // Check if vertices are antipodal + bool antipodal = dot(n1, n2) < -0.99f; + + assert(false && "Spherical lune detected: antipodal silhouette edge"); + } +#endif + + DebugDataBuffer[0].maxTrianglesExcceded = data.count > MAX_TRIANGLES; + return data; +} + +float32_t3 sampleFromData(SamplingData data, ClippedSilhouette silhouette, float32_t2 xi, out float32_t pdf, out uint32_t selectedIdx) +{ + if (data.count == 0 || data.totalWeight <= 0.0f) + { + pdf = 0; + selectedIdx = 0; + return float32_t3(0, 0, 1); + } + + // Select triangle using uniform random sampling weighted by importance + float32_t toFind = xi.x * data.totalWeight; + uint32_t triIdx = 0; + float32_t cumulativeWeight = 0.0f; + float32_t prevCumulativeWeight = 0.0f; + + NBL_UNROLL + for (uint32_t i = 0; i < data.count; i++) + { + prevCumulativeWeight = cumulativeWeight; + cumulativeWeight += data.triangleWeights[i]; + if (toFind <= cumulativeWeight) + { + triIdx = i; + break; + } + } + + selectedIdx = triIdx; + + // Remap xi.x to [0,1] within the selected triangle's weight range + float32_t triMin = prevCumulativeWeight; + float32_t triMax = cumulativeWeight; + float32_t triWeight = triMax - triMin; + float32_t u = (toFind - triMin) / max(triWeight, 1e-7f); + + // Reconstruct the triangle using the stored vertex index + uint32_t vertexIdx = data.triangleIndices[triIdx]; // We need to account for skipped degenerate triangles. + float32_t3 v0 = silhouette.vertices[0]; + float32_t3 v1 = silhouette.vertices[vertexIdx]; + float32_t3 v2 = silhouette.vertices[vertexIdx + 1]; + float32_t3 origin = float32_t3(0, 0, 0); + + shapes::SphericalTriangle shapeTri = shapes::SphericalTriangle::create(v0, v1, v2, origin); + sampling::SphericalTriangle samplingTri = sampling::SphericalTriangle::create(shapeTri); + + // Sample from the selected triangle using remapped u and original xi.y + float32_t rcpPdf; + float32_t3 direction = samplingTri.generate(rcpPdf, float32_t2(u, xi.y)); + + float32_t trianglePdf = 1.0f / rcpPdf; + pdf = trianglePdf * (data.triangleWeights[triIdx] / data.totalWeight); + + return normalize(direction); +} + +float32_t4 visualizeSamples(float32_t2 screenUV, float32_t3 spherePos, ClippedSilhouette silhouette, + int32_t samplingMode, SamplingData samplingData, int32_t numSamples) +{ + float32_t4 accumColor = 0; + + if (samplingData.count == 0) + return 0; + + float32_t2 pssSize = float32_t2(0.3, 0.3); // 30% of screen + float32_t2 pssPos = float32_t2(0.01, 0.01); // Offset from corner + bool isInsidePSS = all(and(screenUV >= pssPos, screenUV <= (pssPos + pssSize))); + + for (int32_t i = 0; i < numSamples; i++) + { + nbl::hlsl::random::PCG32 seedGen = nbl::hlsl::random::PCG32::construct(pc.frameIndex * 65536u + i); + const uint32_t seed1 = seedGen(); + const uint32_t seed2 = seedGen(); + nbl::hlsl::Xoroshiro64StarStar rnd = nbl::hlsl::Xoroshiro64StarStar::construct(uint32_t2(seed1, seed2)); + float32_t2 xi = nextRandomUnorm2(rnd); + + float32_t pdf; + uint32_t triIdx; + float32_t3 sampleDir = sampleFromData(samplingData, silhouette, xi, pdf, triIdx); + + float32_t dist3D = distance(sampleDir, normalize(spherePos)); + float32_t alpha3D = 1.0f - smoothstep(0.0f, 0.02f, dist3D); + + if (alpha3D > 0.0f && !isInsidePSS) + { + float32_t3 sampleColor = colorLUT[triIdx].rgb; + accumColor += float32_t4(sampleColor * alpha3D, alpha3D); + } + + if (isInsidePSS) + { + // Map the raw xi to the PSS square dimensions + float32_t2 xiPixelPos = pssPos + xi * pssSize; + float32_t dist2D = distance(screenUV, xiPixelPos); + + float32_t alpha2D = drawCross2D(screenUV, xiPixelPos, 0.005f, 0.001f); + if (alpha2D > 0.0f) + { + float32_t3 sampleColor = colorLUT[triIdx].rgb; + accumColor += float32_t4(sampleColor * alpha2D, alpha2D); + } + } + } + + // just the outline of the PSS + if (isInsidePSS && accumColor.a < 0.1) + accumColor = float32_t4(0.1, 0.1, 0.1, 1.0); + + return accumColor; +} +#endif diff --git a/73_SolidAngleVisualizer/app_resources/hlsl/SolidAngleVis.frag.hlsl b/73_SolidAngleVisualizer/app_resources/hlsl/SolidAngleVis.frag.hlsl new file mode 100644 index 000000000..31cbe577a --- /dev/null +++ b/73_SolidAngleVisualizer/app_resources/hlsl/SolidAngleVis.frag.hlsl @@ -0,0 +1,466 @@ +#pragma wave shader_stage(fragment) + +#include "common.hlsl" +#include +#include "utils.hlsl" + +using namespace nbl::hlsl; +using namespace ext::FullScreenTriangle; + +[[vk::push_constant]] struct PushConstants pc; +[[vk::binding(0, 0)]] RWStructuredBuffer DebugDataBuffer; + +static const float CIRCLE_RADIUS = 0.5f; + +// --- Geometry Utils --- +struct ClippedSilhouette +{ + float32_t3 vertices[7]; + uint32_t count; +}; + +static const float32_t3 constCorners[8] = { + float32_t3(-1, -1, -1), float32_t3(1, -1, -1), float32_t3(-1, 1, -1), float32_t3(1, 1, -1), + float32_t3(-1, -1, 1), float32_t3(1, -1, 1), float32_t3(-1, 1, 1), float32_t3(1, 1, 1)}; + +static const int32_t2 allEdges[12] = { + {0, 1}, + {2, 3}, + {4, 5}, + {6, 7}, // X axis + {0, 2}, + {1, 3}, + {4, 6}, + {5, 7}, // Y axis + {0, 4}, + {1, 5}, + {2, 6}, + {3, 7}, // Z axis +}; + +// Maps face index (0-5) to its 4 corner indices in CCW order +static const uint32_t faceToCorners[6][4] = { + {0, 2, 3, 1}, // Face 0: Z- + {4, 5, 7, 6}, // Face 1: Z+ + {0, 4, 6, 2}, // Face 2: X- + {1, 3, 7, 5}, // Face 3: X+ + {0, 1, 5, 4}, // Face 4: Y- + {2, 6, 7, 3} // Face 5: Y+ +}; + +static float32_t3 corners[8]; +static float32_t3 faceCenters[6] = { + float32_t3(0, 0, 0), float32_t3(0, 0, 0), float32_t3(0, 0, 0), + float32_t3(0, 0, 0), float32_t3(0, 0, 0), float32_t3(0, 0, 0)}; + +static const float32_t3 localNormals[6] = { + float32_t3(0, 0, -1), // Face 0 (Z-) + float32_t3(0, 0, 1), // Face 1 (Z+) + float32_t3(-1, 0, 0), // Face 2 (X-) + float32_t3(1, 0, 0), // Face 3 (X+) + float32_t3(0, -1, 0), // Face 4 (Y-) + float32_t3(0, 1, 0) // Face 5 (Y+) +}; + +// TODO: unused, remove later +// Vertices are ordered CCW relative to the camera view. +static const int32_t silhouettes[27][7] = { + {6, 1, 3, 2, 6, 4, 5}, // 0: Black + {6, 2, 6, 4, 5, 7, 3}, // 1: White + {6, 0, 4, 5, 7, 3, 2}, // 2: Gray + {6, 1, 3, 7, 6, 4, 5}, // 3: Red + {4, 4, 5, 7, 6, -1, -1}, // 4: Green + {6, 0, 4, 5, 7, 6, 2}, // 5: Blue + {6, 0, 1, 3, 7, 6, 4}, // 6: Yellow + {6, 0, 1, 5, 7, 6, 4}, // 7: Magenta + {6, 0, 1, 5, 7, 6, 2}, // 8: Cyan + {6, 1, 3, 2, 6, 7, 5}, // 9: Orange + {4, 2, 6, 7, 3, -1, -1}, // 10: Light Orange + {6, 0, 4, 6, 7, 3, 2}, // 11: Dark Orange + {4, 1, 3, 7, 5, -1, -1}, // 12: Pink + {6, 0, 4, 6, 7, 3, 2}, // 13: Light Pink + {4, 0, 4, 6, 2, -1, -1}, // 14: Deep Rose + {6, 0, 1, 3, 7, 5, 4}, // 15: Purple + {4, 0, 1, 5, 4, -1, -1}, // 16: Light Purple + {6, 0, 1, 5, 4, 6, 2}, // 17: Indigo + {6, 0, 2, 6, 7, 5, 1}, // 18: Dark Green + {6, 0, 2, 6, 7, 3, 1}, // 19: Lime + {6, 0, 4, 6, 7, 3, 1}, // 20: Forest Green + {6, 0, 2, 3, 7, 5, 1}, // 21: Navy + {4, 0, 2, 3, 1, -1, -1}, // 22: Sky Blue + {6, 0, 4, 6, 2, 3, 1}, // 23: Teal + {6, 0, 2, 3, 7, 5, 4}, // 24: Brown + {6, 0, 2, 3, 1, 5, 4}, // 25: Tan/Beige + {6, 1, 5, 4, 6, 2, 3} // 26: Dark Brown +}; + +// Binary packed silhouettes +static const uint32_t binSilhouettes[27] = { + 0b11000000000000101100110010011001, + 0b11000000000000011111101100110010, + 0b11000000000000010011111101100000, + 0b11000000000000101100110111011001, + 0b10000000000000000000110111101100, + 0b11000000000000010110111101100000, + 0b11000000000000100110111011001000, + 0b11000000000000100110111101001000, + 0b11000000000000010110111101001000, + 0b11000000000000101111110010011001, + 0b10000000000000000000011111110010, + 0b11000000000000010011111110100000, + 0b10000000000000000000101111011001, + 0b11000000000000010011111110100000, + 0b10000000000000000000010110100000, + 0b11000000000000100101111011001000, + 0b10000000000000000000100101001000, + 0b11000000000000010110100101001000, + 0b11000000000000001101111110010000, + 0b11000000000000001011111110010000, + 0b11000000000000001011111110100000, + 0b11000000000000001101111011010000, + 0b10000000000000000000001011010000, + 0b11000000000000001011010110100000, + 0b11000000000000100101111011010000, + 0b11000000000000100101001011010000, + 0b11000000000000011010110100101001, +}; + +int32_t getSilhouetteVertex(uint32_t packedSil, int32_t index) +{ + return (packedSil >> (3 * index)) & 0x7; +} + +// Get silhouette size +int32_t getSilhouetteSize(uint32_t sil) +{ + return (sil >> 29) & 0x7; +} + +// Check if vertex has negative z +bool getVertexZNeg(int32_t vertexIdx) +{ +#if FAST + float32_t3 localPos = float32_t3( + (vertexIdx & 1) ? 1.0f : -1.0f, + (vertexIdx & 2) ? 1.0f : -1.0f, + (vertexIdx & 4) ? 1.0f : -1.0f); + + float transformedZ = dot(pc.modelMatrix[2].xyz, localPos) + pc.modelMatrix[2].w; + return transformedZ < 0.0f; +#else + return corners[vertexIdx].z < 0.0f; +#endif +} + +// Get world position of cube vertex +float32_t3 getVertex(int32_t vertexIdx) +{ +#if FAST + // Reconstruct local cube corner from index bits + float sx = (vertexIdx & 1) ? 1.0f : -1.0f; + float sy = (vertexIdx & 2) ? 1.0f : -1.0f; + float sz = (vertexIdx & 4) ? 1.0f : -1.0f; + + float32_t4x3 model = transpose(pc.modelMatrix); + + // Transform to world + // Full position, not just Z like getVertexZNeg + return model[0].xyz * sx + + model[1].xyz * sy + + model[2].xyz * sz + + model[3].xyz; + // return mul(pc.modelMatrix, float32_t4(sx, sy, sz, 1.0f)); +#else + return corners[vertexIdx]; +#endif +} + +#include "Drawing.hlsl" +#include "Sampling.hlsl" + +void setDebugData(uint32_t sil, int32_t3 region, int32_t configIndex) +{ +#if DEBUG_DATA + DebugDataBuffer[0].region = uint32_t3(region); + DebugDataBuffer[0].silhouetteIndex = uint32_t(configIndex); + DebugDataBuffer[0].silhouetteVertexCount = uint32_t(getSilhouetteSize(sil)); + for (int32_t i = 0; i < 6; i++) + { + DebugDataBuffer[0].vertices[i] = uint32_t(getSilhouetteVertex(sil, i)); + } + DebugDataBuffer[0].silhouette = sil; +#endif +} + +float32_t2 toCircleSpace(float32_t2 uv) +{ + float32_t2 p = uv * 2.0f - 1.0f; + float aspect = pc.viewport.z / pc.viewport.w; + p.x *= aspect; + return p; +} + +uint32_t packSilhouette(const int32_t s[7]) +{ + uint32_t packed = 0; + int32_t size = s[0] & 0x7; // 3 bits for size + + // Pack vertices LSB-first (vertex1 in lowest 3 bits above size) + for (int32_t i = 1; i <= 6; ++i) + { + int32_t v = s[i]; + if (v < 0) + v = 0; // replace unused vertices with 0 + packed |= (v & 0x7) << (3 * (i - 1)); // vertex i-1 shifted by 3*(i-1) + } + + // Put size in the MSB (bits 29-31 for a 32-bit uint32_t, leaving 29 bits for vertices) + packed |= (size & 0x7) << 29; + + return packed; +} + +void computeCubeGeo() +{ + for (int32_t i = 0; i < 8; i++) + corners[i] = mul(pc.modelMatrix, float32_t4(constCorners[i], 1.0f)).xyz; + + for (int32_t f = 0; f < 6; f++) + { + faceCenters[f] = float32_t3(0, 0, 0); + for (int32_t v = 0; v < 4; v++) + faceCenters[f] += corners[faceToCorners[f][v]]; + faceCenters[f] /= 4.0f; + } +} + +// Helper to draw an edge with proper color mapping +float32_t4 drawEdge(int32_t originalEdgeIdx, float32_t3 pts[2], float32_t3 spherePos, float aaWidth, float width = 0.01f) +{ + float32_t4 edgeContribution = drawGreatCircleArc(spherePos, pts, aaWidth, width); + return float32_t4(colorLUT[originalEdgeIdx] * edgeContribution.a, edgeContribution.a); +}; + +float32_t4 computeSilhouette(uint32_t vertexCount, uint32_t sil, float32_t3 spherePos, float aaWidth, out ClippedSilhouette silhouette) +{ + float32_t4 color = float32_t4(0, 0, 0, 0); + silhouette.count = 0; + + // Build clip mask (z < 0) + int32_t clipMask = 0u; + NBL_UNROLL + for (int32_t i = 0; i < 4; i++) + clipMask |= (getVertexZNeg(getSilhouetteVertex(sil, i)) ? 1u : 0u) << i; + + if (vertexCount == 6) + { + NBL_UNROLL + for (int32_t i = 4; i < 6; i++) + clipMask |= (getVertexZNeg(getSilhouetteVertex(sil, i)) ? 1u : 0u) << i; + } + + int32_t clipCount = countbits(clipMask); + +#if 0 + // Early exit if fully clipped + if (clipCount == vertexCount) + return color; + + // No clipping needed - fast path + if (clipCount == 0) + { + for (int32_t i = 0; i < vertexCount; i++) + { + int32_t i0 = i; + int32_t i1 = (i + 1) % vertexCount; + + float32_t3 v0 = getVertex(getSilhouetteVertex(sil, i0)); + float32_t3 v1 = getVertex(getSilhouetteVertex(sil, i1)); + float32_t3 pts[2] = {v0, v1}; + + color += drawEdge(i1, pts, spherePos, aaWidth); + } + return color; + } +#endif + + // Rotate clip mask so positives come first + uint32_t invertedMask = ~clipMask & ((1u << vertexCount) - 1u); + bool wrapAround = ((clipMask & 1u) != 0u) && + ((clipMask & (1u << (vertexCount - 1))) != 0u); + int32_t rotateAmount = wrapAround + ? firstbitlow(invertedMask) // -> First POSITIVE + : firstbithigh(clipMask) + 1; // -> First vertex AFTER last negative + + uint32_t rotatedClipMask = rotr(clipMask, rotateAmount, vertexCount); + uint32_t rotatedSil = rotr(sil, rotateAmount * 3, vertexCount * 3); + + int32_t positiveCount = vertexCount - clipCount; + + // ALWAYS compute both clip points + int32_t lastPosIdx = positiveCount - 1; + int32_t firstNegIdx = positiveCount; + float32_t3 vLastPos = getVertex(getSilhouetteVertex(rotatedSil, lastPosIdx)); + float32_t3 vFirstNeg = getVertex(getSilhouetteVertex(rotatedSil, firstNegIdx)); + float t = vLastPos.z / (vLastPos.z - vFirstNeg.z); + float32_t3 clipA = lerp(vLastPos, vFirstNeg, t); + + float32_t3 vLastNeg = getVertex(getSilhouetteVertex(rotatedSil, vertexCount - 1)); + float32_t3 vFirstPos = getVertex(getSilhouetteVertex(rotatedSil, 0)); + t = vLastNeg.z / (vLastNeg.z - vFirstPos.z); + float32_t3 clipB = lerp(vLastNeg, vFirstPos, t); + + // Draw positive edges + NBL_UNROLL + for (int32_t i = 0; i < positiveCount; i++) + { + float32_t3 v0 = getVertex(getSilhouetteVertex(rotatedSil, i)); + + // ONLY use clipA if we are at the end of the positive run AND there's a clip + bool isLastPositive = (i == positiveCount - 1); + bool useClipA = (clipCount > 0) && isLastPositive; + + // If not using clipA, wrap around to the next vertex + float32_t3 v1 = useClipA ? clipA : getVertex(getSilhouetteVertex(rotatedSil, (i + 1) % vertexCount)); + + float32_t3 pts[2] = {v0, v1}; + color += drawEdge((i + 1) % vertexCount, pts, spherePos, aaWidth); + + silhouette.vertices[silhouette.count++] = v0; + } + + if (clipCount > 0 && clipCount < vertexCount) + { + // NP edge + float32_t3 vFirst = getVertex(getSilhouetteVertex(rotatedSil, 0)); + float32_t3 npPts[2] = {clipB, vFirst}; + color += drawEdge(0, npPts, spherePos, aaWidth); + + // Horizon arc + float32_t3 arcPts[2] = {clipA, clipB}; + color += drawEdge(23, arcPts, spherePos, aaWidth, 0.6f); + + silhouette.vertices[silhouette.count++] = clipA; + silhouette.vertices[silhouette.count++] = clipB; + } + +#if DEBUG_DATA + DebugDataBuffer[0].clipMask = clipMask; + DebugDataBuffer[0].clipCount = clipCount; + DebugDataBuffer[0].rotatedClipMask = rotatedClipMask; + DebugDataBuffer[0].rotateAmount = rotateAmount; + DebugDataBuffer[0].positiveVertCount = positiveCount; + DebugDataBuffer[0].wrapAround = (uint32_t)wrapAround; + DebugDataBuffer[0].rotatedSil = rotatedSil; + +#endif + return color; +} + +[[vk::location(0)]] float32_t4 main(SVertexAttributes vx) : SV_Target0 +{ + float32_t4 color = float32_t4(0, 0, 0, 0); + for (int32_t i = 0; i < 1; i++) + { + float aaWidth = length(float32_t2(ddx(vx.uv.x), ddy(vx.uv.y))); + float32_t2 p = toCircleSpace(vx.uv); + + float32_t2 normalized = p / CIRCLE_RADIUS; + float r2 = dot(normalized, normalized); + + float32_t3 spherePos; + if (r2 <= 1.0f) + { + spherePos = float32_t3(normalized.x, normalized.y, sqrt(1.0f - r2)); + } + else + { + float uv2Plus1 = r2 + 1.0f; + spherePos = float32_t3(normalized.x * 2.0f, normalized.y * 2.0f, 1.0f - r2) / uv2Plus1; + } + spherePos = normalize(spherePos); + + computeCubeGeo(); + + float32_t4x3 columnModel = transpose(pc.modelMatrix); + float32_t3 obbCenter = columnModel[3].xyz; + float32_t3x3 upper3x3 = (float32_t3x3)columnModel; + float32_t3 rcpSqScales = rcp(float32_t3( + dot(upper3x3[0], upper3x3[0]), + dot(upper3x3[1], upper3x3[1]), + dot(upper3x3[2], upper3x3[2]))); + float32_t3 normalizedProj = mul(upper3x3, obbCenter) * rcpSqScales; + + int32_t3 region = int32_t3( + normalizedProj.x < -1.0f ? 0 : (normalizedProj.x > 1.0f ? 2 : 1), + normalizedProj.y < -1.0f ? 0 : (normalizedProj.y > 1.0f ? 2 : 1), + normalizedProj.z < -1.0f ? 0 : (normalizedProj.z > 1.0f ? 2 : 1)); + + int32_t configIndex = region.x + region.y * 3 + region.z * 9; + + // uint32_t sil = packSilhouette(silhouettes[configIndex]); + uint32_t sil = binSilhouettes[configIndex]; + + int32_t vertexCount = getSilhouetteSize(sil); + + uint32_t silEdgeMask = 0; // TODO: take from 'fast' computeSilhouette() +#if DEBUG_DATA + { + for (int32_t i = 0; i < vertexCount; i++) + { + int32_t vIdx = i % vertexCount; + int32_t v1Idx = (i + 1) % vertexCount; + + int32_t v0Corner = getSilhouetteVertex(sil, vIdx); + int32_t v1Corner = getSilhouetteVertex(sil, v1Idx); + // Mark edge as part of silhouette + for (int32_t e = 0; e < 12; e++) + { + int32_t2 edge = allEdges[e]; + if ((edge.x == v0Corner && edge.y == v1Corner) || + (edge.x == v1Corner && edge.y == v0Corner)) + { + silEdgeMask |= (1u << e); + } + } + } + validateEdgeVisibility(sil, vertexCount, silEdgeMask); + } +#endif + + uint32_t positiveCount = 0; + + ClippedSilhouette silhouette; + color += computeSilhouette(vertexCount, sil, spherePos, aaWidth, silhouette); + // Draw clipped silhouette vertices + // color += drawClippedSilhouetteVertices(p, silhouette, aaWidth); + + SamplingData samplingData = buildSamplingDataFromSilhouette(silhouette, pc.samplingMode); + + uint32_t faceIndices[3]; + uint32_t visibleFaceCount = getVisibleFaces(region, faceIndices); + + // For debugging: Draw a small indicator of which faces are found + // color += drawVisibleFaceOverlay(spherePos, region, aaWidth); + + // color += drawFaces(spherePos, aaWidth); + + // Draw samples on sphere + color += visualizeSamples(vx.uv, spherePos, silhouette, pc.samplingMode, samplingData, 64); + + // Or draw 2D sample space (in a separate viewport) + // color += visualizePrimarySampleSpace(vx.uv, pc.samplingMode, 64, aaWidth); + + setDebugData(sil, region, configIndex); + // color += drawHiddenEdges(spherePos, silEdgeMask, aaWidth); + color += drawCorners(p, aaWidth); + color += drawRing(p, aaWidth); + + if (all(vx.uv >= float32_t2(0.49f, 0.49f)) && all(vx.uv <= float32_t2(0.51f, 0.51f))) + { + return float32_t4(colorLUT[configIndex], 1.0f); + } + } + + return color; +} \ No newline at end of file diff --git a/73_SolidAngleVisualizer/app_resources/hlsl/common.hlsl b/73_SolidAngleVisualizer/app_resources/hlsl/common.hlsl new file mode 100644 index 000000000..dd0ab2d99 --- /dev/null +++ b/73_SolidAngleVisualizer/app_resources/hlsl/common.hlsl @@ -0,0 +1,65 @@ +#ifndef _SOLID_ANGLE_VIS_COMMON_HLSL_ +#define _SOLID_ANGLE_VIS_COMMON_HLSL_ +#include "nbl/builtin/hlsl/cpp_compat.hlsl" + +#define DEBUG_DATA 1 +#define FAST 1 + +namespace nbl +{ + namespace hlsl + { + + struct ResultData + { + uint32_t3 region; + uint32_t silhouetteIndex; + + uint32_t silhouetteVertexCount; + uint32_t silhouette; + uint32_t positiveVertCount; + uint32_t edgeVisibilityMismatch; + + uint32_t clipMask; + uint32_t clipCount; + uint32_t rotatedSil; + uint32_t wrapAround; + + uint32_t rotatedClipMask; + uint32_t rotateAmount; + uint32_t maxTrianglesExcceded; + + uint32_t vertices[6]; + }; + + struct PushConstants + { + float32_t3x4 modelMatrix; + float32_t4 viewport; + uint32_t samplingMode; + uint32_t frameIndex; + }; + // Sampling mode enum +#define SAMPLING_MODE_SOLID_ANGLE 0 +#define SAMPLING_MODE_PROJECTED_SOLID_ANGLE 1 + + static const float32_t3 colorLUT[27] = { + float32_t3(0, 0, 0), float32_t3(0.5, 0.5, 0.5), + float32_t3(1, 0, 0), float32_t3(0, 1, 0), float32_t3(0, 0, 1), + float32_t3(1, 1, 0), float32_t3(1, 0, 1), float32_t3(0, 1, 1), + float32_t3(1, 0.5, 0), float32_t3(1, 0.65, 0), float32_t3(0.8, 0.4, 0), + float32_t3(1, 0.4, 0.7), float32_t3(1, 0.75, 0.8), float32_t3(0.7, 0.1, 0.3), + float32_t3(0.5, 0, 0.5), float32_t3(0.6, 0.4, 0.8), float32_t3(0.3, 0, 0.5), + float32_t3(0, 0.5, 0), float32_t3(0.5, 1, 0), float32_t3(0, 0.5, 0.25), + float32_t3(0, 0, 0.5), float32_t3(0.3, 0.7, 1), float32_t3(0, 0.4, 0.6), + float32_t3(0.6, 0.4, 0.2), float32_t3(0.8, 0.7, 0.3), float32_t3(0.4, 0.3, 0.1), float32_t3(1, 1, 1)}; + +#ifndef __HLSL_VERSION + static const char *colorNames[27] = {"Black", "Gray", "Red", "Green", "Blue", "Yellow", "Magenta", "Cyan", + "Orange", "Light Orange", "Dark Orange", "Pink", "Light Pink", "Deep Rose", "Purple", "Light Purple", + "Indigo", "Dark Green", "Lime", "Forest Green", "Navy", "Sky Blue", "Teal", "Brown", + "Tan/Beige", "Dark Brown", "White"}; +#endif // __HLSL_VERSION + } +} +#endif // _SOLID_ANGLE_VIS_COMMON_HLSL_ diff --git a/73_SolidAngleVisualizer/app_resources/hlsl/utils.hlsl b/73_SolidAngleVisualizer/app_resources/hlsl/utils.hlsl new file mode 100644 index 000000000..4031e048f --- /dev/null +++ b/73_SolidAngleVisualizer/app_resources/hlsl/utils.hlsl @@ -0,0 +1,23 @@ +#ifndef _UTILS_HLSL_ +#define _UTILS_HLSL_ + +// TODO: implemented somewhere else? +// Bit rotation helpers +uint32_t rotl(uint32_t value, uint32_t bits, uint32_t width) +{ + bits = bits % width; + uint32_t mask = (1u << width) - 1u; + value &= mask; + return ((value << bits) | (value >> (width - bits))) & mask; +} + +uint32_t rotr(uint32_t value, uint32_t bits, uint32_t width) +{ + bits = bits % width; + uint32_t mask = (1u << width) - 1u; + value &= mask; + return ((value >> bits) | (value << (width - bits))) & mask; +} + + +#endif // _UTILS_HLSL_ diff --git a/73_SolidAngleVisualizer/config.json.template b/73_SolidAngleVisualizer/config.json.template new file mode 100644 index 000000000..f961745c1 --- /dev/null +++ b/73_SolidAngleVisualizer/config.json.template @@ -0,0 +1,28 @@ +{ + "enableParallelBuild": true, + "threadsPerBuildProcess" : 2, + "isExecuted": false, + "scriptPath": "", + "cmake": { + "configurations": [ "Release", "Debug", "RelWithDebInfo" ], + "buildModes": [], + "requiredOptions": [] + }, + "profiles": [ + { + "backend": "vulkan", + "platform": "windows", + "buildModes": [], + "runConfiguration": "Release", + "gpuArchitectures": [] + } + ], + "dependencies": [], + "data": [ + { + "dependencies": [], + "command": [""], + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/73_SolidAngleVisualizer/include/common.hpp b/73_SolidAngleVisualizer/include/common.hpp new file mode 100644 index 000000000..2e8e985dd --- /dev/null +++ b/73_SolidAngleVisualizer/include/common.hpp @@ -0,0 +1,20 @@ +#ifndef _NBL_THIS_EXAMPLE_COMMON_H_INCLUDED_ +#define _NBL_THIS_EXAMPLE_COMMON_H_INCLUDED_ + + +#include "nbl/examples/examples.hpp" + +// the example's headers +#include "transform.hpp" +#include "nbl/builtin/hlsl/matrix_utils/transformation_matrix_utils.hlsl" + +using namespace nbl; +using namespace nbl::core; +using namespace nbl::hlsl; +using namespace nbl::system; +using namespace nbl::asset; +using namespace nbl::ui; +using namespace nbl::video; +using namespace nbl::examples; + +#endif // _NBL_THIS_EXAMPLE_COMMON_H_INCLUDED_ \ No newline at end of file diff --git a/73_SolidAngleVisualizer/include/transform.hpp b/73_SolidAngleVisualizer/include/transform.hpp new file mode 100644 index 000000000..e1ffcd764 --- /dev/null +++ b/73_SolidAngleVisualizer/include/transform.hpp @@ -0,0 +1,213 @@ +#ifndef _NBL_THIS_EXAMPLE_TRANSFORM_H_INCLUDED_ +#define _NBL_THIS_EXAMPLE_TRANSFORM_H_INCLUDED_ + +#include "nbl/ui/ICursorControl.h" +#include "nbl/ext/ImGui/ImGui.h" +#include "imgui/imgui_internal.h" +#include "imguizmo/ImGuizmo.h" + +struct TransformRequestParams +{ + uint8_t sceneTexDescIx = ~0; + bool useWindow = true, editTransformDecomposition = false, enableViewManipulate = true; +}; + +struct TransformReturnInfo +{ + nbl::hlsl::uint16_t2 sceneResolution = { 1, 1 }; + bool allowCameraMovement = false; +}; + +TransformReturnInfo EditTransform(float* cameraView, const float* cameraProjection, float* matrix, const TransformRequestParams& params) +{ + static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::TRANSLATE); + static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::LOCAL); + static bool useSnap = false; + static float snap[3] = { 1.f, 1.f, 1.f }; + static float bounds[] = { -0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f }; + static float boundsSnap[] = { 0.1f, 0.1f, 0.1f }; + static bool boundSizing = false; + static bool boundSizingSnap = false; + + ImGui::Text("Use gizmo (T/R/G) or ViewManipulate widget to transform the cube"); + + if (params.editTransformDecomposition) + { + if (ImGui::IsKeyPressed(ImGuiKey_T)) + mCurrentGizmoOperation = ImGuizmo::TRANSLATE; + if (ImGui::IsKeyPressed(ImGuiKey_R)) + mCurrentGizmoOperation = ImGuizmo::ROTATE; + if (ImGui::IsKeyPressed(ImGuiKey_G)) + mCurrentGizmoOperation = ImGuizmo::SCALE; + if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE)) + mCurrentGizmoOperation = ImGuizmo::TRANSLATE; + ImGui::SameLine(); + if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE)) + mCurrentGizmoOperation = ImGuizmo::ROTATE; + ImGui::SameLine(); + if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE)) + mCurrentGizmoOperation = ImGuizmo::SCALE; + if (ImGui::RadioButton("Universal", mCurrentGizmoOperation == ImGuizmo::UNIVERSAL)) + mCurrentGizmoOperation = ImGuizmo::UNIVERSAL; + + // For UI editing, decompose temporarily + float matrixTranslation[3], matrixRotation[3], matrixScale[3]; + ImGuizmo::DecomposeMatrixToComponents(matrix, matrixTranslation, matrixRotation, matrixScale); + ImGui::DragFloat3("Tr", matrixTranslation, 0.01f); + ImGui::DragFloat3("Rt", matrixRotation, 0.01f); + ImGui::DragFloat3("Sc", matrixScale, 0.01f); + ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix); + + if (mCurrentGizmoOperation != ImGuizmo::SCALE) + { + if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL)) + mCurrentGizmoMode = ImGuizmo::LOCAL; + ImGui::SameLine(); + if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD)) + mCurrentGizmoMode = ImGuizmo::WORLD; + } + if (ImGui::IsKeyPressed(ImGuiKey_S) && ImGui::IsKeyPressed(ImGuiKey_LeftShift)) + useSnap = !useSnap; + ImGui::Checkbox("##UseSnap", &useSnap); + ImGui::SameLine(); + + switch (mCurrentGizmoOperation) + { + case ImGuizmo::TRANSLATE: + ImGui::InputFloat3("Snap", &snap[0]); + break; + case ImGuizmo::ROTATE: + ImGui::InputFloat("Angle Snap", &snap[0]); + break; + case ImGuizmo::SCALE: + ImGui::InputFloat("Scale Snap", &snap[0]); + break; + } + ImGui::Checkbox("Bound Sizing", &boundSizing); + if (boundSizing) + { + ImGui::PushID(3); + ImGui::Checkbox("##BoundSizing", &boundSizingSnap); + ImGui::SameLine(); + ImGui::InputFloat3("Snap", boundsSnap); + ImGui::PopID(); + } + } + + ImGuiIO& io = ImGui::GetIO(); + float viewManipulateRight = io.DisplaySize.x; + float viewManipulateTop = 0; + bool isWindowHovered = false; + static ImGuiWindowFlags gizmoWindowFlags = 0; + + /* + for the "useWindow" case we just render to a gui area, + otherwise to fake full screen transparent window + + note that for both cases we make sure gizmo being + rendered is aligned to our texture scene using + imgui "cursor" screen positions + */ + // TODO: this shouldn't be handled here I think + SImResourceInfo info; + info.textureID = params.sceneTexDescIx; + info.samplerIx = (uint16_t)nbl::ext::imgui::UI::DefaultSamplerIx::USER; + + TransformReturnInfo retval; + if (params.useWindow) + { + ImGui::SetNextWindowSize(ImVec2(800, 800), ImGuiCond_Appearing); + ImGui::SetNextWindowPos(ImVec2(400, 20), ImGuiCond_Appearing); + ImGui::PushStyleColor(ImGuiCol_WindowBg, (ImVec4)ImColor(0.35f, 0.3f, 0.3f)); + ImGui::Begin("Gizmo", 0, gizmoWindowFlags); + ImGuizmo::SetDrawlist(); + + ImVec2 contentRegionSize = ImGui::GetContentRegionAvail(); + ImVec2 windowPos = ImGui::GetWindowPos(); + ImVec2 cursorPos = ImGui::GetCursorScreenPos(); + isWindowHovered = ImGui::IsWindowHovered(); + + ImGui::Image(info, contentRegionSize); + ImGuizmo::SetRect(cursorPos.x, cursorPos.y, contentRegionSize.x, contentRegionSize.y); + retval.sceneResolution = { contentRegionSize.x,contentRegionSize.y }; + + viewManipulateRight = cursorPos.x + contentRegionSize.x; + viewManipulateTop = cursorPos.y; + + ImGuiWindow* window = ImGui::GetCurrentWindow(); + gizmoWindowFlags = (isWindowHovered && ImGui::IsMouseHoveringRect(window->InnerRect.Min, window->InnerRect.Max) ? ImGuiWindowFlags_NoMove : 0); + } + else + { + ImGui::SetNextWindowPos(ImVec2(0, 0)); + ImGui::SetNextWindowSize(io.DisplaySize); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0)); // fully transparent fake window + ImGui::Begin("FullScreenWindow", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoInputs); + + ImVec2 contentRegionSize = ImGui::GetContentRegionAvail(); + ImVec2 cursorPos = ImGui::GetCursorScreenPos(); + isWindowHovered = ImGui::IsWindowHovered(); + + ImGui::Image(info, contentRegionSize); + ImGuizmo::SetRect(cursorPos.x, cursorPos.y, contentRegionSize.x, contentRegionSize.y); + retval.sceneResolution = { contentRegionSize.x,contentRegionSize.y }; + + viewManipulateRight = cursorPos.x + contentRegionSize.x; + viewManipulateTop = cursorPos.y; + } + + // Standard Manipulate gizmo - let ImGuizmo modify the matrix directly + ImGuizmo::Manipulate(cameraView, cameraProjection, mCurrentGizmoOperation, mCurrentGizmoMode, matrix, NULL, useSnap ? &snap[0] : NULL, boundSizing ? bounds : NULL, boundSizingSnap ? boundsSnap : NULL); + + retval.allowCameraMovement = isWindowHovered && !ImGuizmo::IsUsing(); + + // ViewManipulate for rotating the view + if (params.enableViewManipulate) + { + // Store original translation and scale before ViewManipulate + // Decompose original matrix + nbl::hlsl::float32_t3 translation, rotation, scale; + ImGuizmo::DecomposeMatrixToComponents(matrix, &translation.x, &rotation.x, &scale.x); + // Create rotation-only matrix + nbl::hlsl::float32_t4x4 temp; + nbl::hlsl::float32_t3 baseTranslation(0.0f); + nbl::hlsl::float32_t3 baseScale(1.0f); + ImGuizmo::RecomposeMatrixFromComponents(&baseTranslation.x, &rotation.x, &baseScale.x, &temp[0][0]); + temp = nbl::hlsl::transpose(temp); + + // Invert to make it "view-like" + nbl::hlsl::float32_t4x4 tempInv = nbl::hlsl::inverse(temp); + + // Create flip matrix (flip X to fix left/right) + nbl::hlsl::float32_t4x4 flip(1.0f); + flip[0][0] = -1.0f; // Flip X axis + + // Apply flip to the inverted matrix + tempInv = nbl::hlsl::mul(nbl::hlsl::mul(flip, tempInv), flip); + + // Manipulate + ImGuizmo::ViewManipulate(&tempInv[0][0], 1.0f, ImVec2(viewManipulateRight - 128, viewManipulateTop), ImVec2(128, 128), 0x10101010); + + // Undo flip (flip is its own inverse, so multiply by flip again) + tempInv = nbl::hlsl::mul(nbl::hlsl::mul(flip, tempInv), flip); + + // Invert back to model space + temp = nbl::hlsl::inverse(tempInv); + temp = nbl::hlsl::transpose(temp); + + // Extract rotation + nbl::hlsl::float32_t3 newRot; + ImGuizmo::DecomposeMatrixToComponents(&temp[0][0], &baseTranslation.x, &newRot.x, &baseScale.x); + // Recompose original matrix with new rotation but keep translation & scale + ImGuizmo::RecomposeMatrixFromComponents(&translation.x, &newRot.x, &scale.x, matrix); + + retval.allowCameraMovement &= isWindowHovered && !ImGuizmo::IsUsingViewManipulate(); + } + + ImGui::End(); + ImGui::PopStyleColor(); + + return retval; +} + +#endif // _NBL_THIS_EXAMPLE_TRANSFORM_H_INCLUDED_ \ No newline at end of file diff --git a/73_SolidAngleVisualizer/main.cpp b/73_SolidAngleVisualizer/main.cpp new file mode 100644 index 000000000..401ab71b3 --- /dev/null +++ b/73_SolidAngleVisualizer/main.cpp @@ -0,0 +1,1431 @@ +// 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 "common.hpp" +#include "app_resources/hlsl/common.hlsl" +#include "nbl/ext/FullScreenTriangle/FullScreenTriangle.h" + +/* +Renders scene texture to an offscreen framebuffer whose color attachment is then sampled into a imgui window. + +Written with Nabla's UI extension and got integrated with ImGuizmo to handle scene's object translations. +*/ +class SolidAngleVisualizer final : public MonoWindowApplication, public BuiltinResourcesApplication +{ + using device_base_t = MonoWindowApplication; + using asset_base_t = BuiltinResourcesApplication; + + inline static std::string SolidAngleVisShaderPath = "app_resources/hlsl/SolidAngleVis.frag.hlsl"; +public: + inline SolidAngleVisualizer(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) + : IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD), + device_base_t({ 2048,1024 }, EF_UNKNOWN, _localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD) { + } + + inline bool onAppInitialized(smart_refctd_ptr&& system) override + { + if (!asset_base_t::onAppInitialized(smart_refctd_ptr(system))) + return false; + if (!device_base_t::onAppInitialized(smart_refctd_ptr(system))) + return false; + + m_semaphore = m_device->createSemaphore(m_realFrameIx); + if (!m_semaphore) + return logFail("Failed to Create a Semaphore!"); + + auto pool = m_device->createCommandPool(getGraphicsQueue()->getFamilyIndex(), IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT); + for (auto i = 0u; i < MaxFramesInFlight; i++) + { + if (!pool) + return logFail("Couldn't create Command Pool!"); + if (!pool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY, { m_cmdBufs.data() + i,1 })) + return logFail("Couldn't create Command Buffer!"); + } + + const uint32_t addtionalBufferOwnershipFamilies[] = { getGraphicsQueue()->getFamilyIndex() }; + m_scene = CGeometryCreatorScene::create( + { + .transferQueue = getTransferUpQueue(), + .utilities = m_utils.get(), + .logger = m_logger.get(), + .addtionalBufferOwnershipFamilies = addtionalBufferOwnershipFamilies + }, + CSimpleDebugRenderer::DefaultPolygonGeometryPatch + ); + + // for the scene drawing pass + { + IGPURenderpass::SCreationParams params = {}; + const IGPURenderpass::SCreationParams::SDepthStencilAttachmentDescription depthAttachments[] = { + {{ + { + .format = sceneRenderDepthFormat, + .samples = IGPUImage::ESCF_1_BIT, + .mayAlias = false + }, + /*.loadOp =*/ {IGPURenderpass::LOAD_OP::CLEAR}, + /*.storeOp =*/ {IGPURenderpass::STORE_OP::STORE}, + /*.initialLayout =*/ {IGPUImage::LAYOUT::UNDEFINED}, + /*.finalLayout =*/ {IGPUImage::LAYOUT::ATTACHMENT_OPTIMAL} + }}, + IGPURenderpass::SCreationParams::DepthStencilAttachmentsEnd + }; + params.depthStencilAttachments = depthAttachments; + const IGPURenderpass::SCreationParams::SColorAttachmentDescription colorAttachments[] = { + {{ + { + .format = finalSceneRenderFormat, + .samples = IGPUImage::E_SAMPLE_COUNT_FLAGS::ESCF_1_BIT, + .mayAlias = false + }, + /*.loadOp =*/ IGPURenderpass::LOAD_OP::CLEAR, + /*.storeOp =*/ IGPURenderpass::STORE_OP::STORE, + /*.initialLayout =*/ IGPUImage::LAYOUT::UNDEFINED, + /*.finalLayout =*/ IGPUImage::LAYOUT::READ_ONLY_OPTIMAL // ImGUI shall read + }}, + IGPURenderpass::SCreationParams::ColorAttachmentsEnd + }; + params.colorAttachments = colorAttachments; + IGPURenderpass::SCreationParams::SSubpassDescription subpasses[] = { + {}, + IGPURenderpass::SCreationParams::SubpassesEnd + }; + subpasses[0].depthStencilAttachment = { {.render = {.attachmentIndex = 0,.layout = IGPUImage::LAYOUT::ATTACHMENT_OPTIMAL}} }; + subpasses[0].colorAttachments[0] = { .render = {.attachmentIndex = 0,.layout = IGPUImage::LAYOUT::ATTACHMENT_OPTIMAL} }; + params.subpasses = subpasses; + + const static IGPURenderpass::SCreationParams::SSubpassDependency dependencies[] = { + // wipe-transition of Color to ATTACHMENT_OPTIMAL and depth + { + .srcSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External, + .dstSubpass = 0, + .memoryBarrier = { + // last place where the depth can get modified in previous frame, `COLOR_ATTACHMENT_OUTPUT_BIT` is implicitly later + // while color is sampled by ImGUI + .srcStageMask = PIPELINE_STAGE_FLAGS::LATE_FRAGMENT_TESTS_BIT | PIPELINE_STAGE_FLAGS::FRAGMENT_SHADER_BIT, + // don't want any writes to be available, as we are clearing both attachments + .srcAccessMask = ACCESS_FLAGS::NONE, + // destination needs to wait as early as possible + // TODO: `COLOR_ATTACHMENT_OUTPUT_BIT` shouldn't be needed, because its a logically later stage, see TODO in `ECommonEnums.h` + .dstStageMask = PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT | PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT, + // because depth and color get cleared first no read mask + .dstAccessMask = ACCESS_FLAGS::DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT + } + // leave view offsets and flags default + }, + { + .srcSubpass = 0, + .dstSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External, + .memoryBarrier = { + // last place where the color can get modified, depth is implicitly earlier + .srcStageMask = PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT, + // only write ops, reads can't be made available, also won't be using depth so don't care about it being visible to anyone else + .srcAccessMask = ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT, + // the ImGUI will sample the color, then next frame we overwrite both attachments + .dstStageMask = PIPELINE_STAGE_FLAGS::FRAGMENT_SHADER_BIT | PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT, + // but we only care about the availability-visibility chain between renderpass and imgui + .dstAccessMask = ACCESS_FLAGS::SAMPLED_READ_BIT + } + // leave view offsets and flags default + }, + IGPURenderpass::SCreationParams::DependenciesEnd + }; + params.dependencies = dependencies; + auto solidAngleRenderpassParams = params; + m_mainRenderpass = m_device->createRenderpass(std::move(params)); + if (!m_mainRenderpass) + return logFail("Failed to create Main Renderpass!"); + + m_solidAngleRenderpass = m_device->createRenderpass(std::move(solidAngleRenderpassParams)); + if (!m_solidAngleRenderpass) + return logFail("Failed to create Solid Angle Renderpass!"); + + } + + const auto& geometries = m_scene->getInitParams().geometries; + m_renderer = CSimpleDebugRenderer::create(m_assetMgr.get(), m_solidAngleRenderpass.get(), 0, { &geometries.front().get(),geometries.size() }); + // special case + { + const auto& pipelines = m_renderer->getInitParams().pipelines; + auto ix = 0u; + for (const auto& name : m_scene->getInitParams().geometryNames) + { + if (name == "Cone") + m_renderer->getGeometry(ix).pipeline = pipelines[CSimpleDebugRenderer::SInitParams::PipelineType::Cone]; + ix++; + } + } + // we'll only display one thing at a time + m_renderer->m_instances.resize(1); + + // Create graphics pipeline + { + auto loadAndCompileHLSLShader = [&](const std::string& pathToShader, const std::string& defineMacro = "") -> smart_refctd_ptr + { + IAssetLoader::SAssetLoadParams lp = {}; + lp.workingDirectory = localInputCWD; + auto assetBundle = m_assetMgr->getAsset(pathToShader, lp); + const auto assets = assetBundle.getContents(); + if (assets.empty()) + { + m_logger->log("Could not load shader: ", ILogger::ELL_ERROR, pathToShader); + std::exit(-1); + } + + auto source = smart_refctd_ptr_static_cast(assets[0]); + // The down-cast should not fail! + assert(source); + + auto compiler = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); + CHLSLCompiler::SOptions options = {}; + options.stage = IShader::E_SHADER_STAGE::ESS_FRAGMENT; + options.preprocessorOptions.targetSpirvVersion = m_device->getPhysicalDevice()->getLimits().spirvVersion; + options.spirvOptimizer = nullptr; +#ifndef _NBL_DEBUG + ISPIRVOptimizer::E_OPTIMIZER_PASS optPasses = ISPIRVOptimizer::EOP_STRIP_DEBUG_INFO; + auto opt = make_smart_refctd_ptr(std::span(&optPasses, 1)); + options.spirvOptimizer = opt.get(); +#endif + options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_LINE_BIT; + options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); + options.preprocessorOptions.logger = m_logger.get(); + options.preprocessorOptions.includeFinder = compiler->getDefaultIncludeFinder(); + + core::vector defines; + if (!defineMacro.empty()) + defines.push_back({ defineMacro, "" }); + + options.preprocessorOptions.extraDefines = defines; + + source = compiler->compileToSPIRV((const char*)source->getContent()->getPointer(), options); + + auto shader = m_device->compileShader({ source.get(), nullptr, nullptr, nullptr }); + if (!shader) + { + m_logger->log("HLSL shader creationed failed: %s!", ILogger::ELL_ERROR, pathToShader); + std::exit(-1); + } + + return shader; + }; + + ext::FullScreenTriangle::ProtoPipeline fsTriProtoPPln(m_assetMgr.get(), m_device.get(), m_logger.get()); + if (!fsTriProtoPPln) + return logFail("Failed to create Full Screen Triangle protopipeline or load its vertex shader!"); + + // Load Fragment Shader + auto fragmentShader = loadAndCompileHLSLShader(SolidAngleVisShaderPath); + if (!fragmentShader) + return logFail("Failed to Load and Compile Fragment Shader: lumaMeterShader!"); + + const IGPUPipelineBase::SShaderSpecInfo fragSpec = { + .shader = fragmentShader.get(), + .entryPoint = "main" + }; + + const asset::SPushConstantRange ranges[] = { { + .stageFlags = hlsl::ShaderStage::ESS_FRAGMENT, + .offset = 0, + .size = sizeof(PushConstants) + } }; + + nbl::video::IGPUDescriptorSetLayout::SBinding bindings[1] = { + { + .binding = 0, + .type = nbl::asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER, + .createFlags = IGPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, + .stageFlags = ShaderStage::ESS_FRAGMENT, + .count = 1 + } + }; + smart_refctd_ptr dsLayout = m_device->createDescriptorSetLayout(bindings); + if (!dsLayout) + logFail("Failed to create a Descriptor Layout!\n"); + + + auto visualizationLayout = m_device->createPipelineLayout(ranges +#if DEBUG_DATA + , dsLayout +#endif + ); + m_visualizationPipeline = fsTriProtoPPln.createPipeline(fragSpec, visualizationLayout.get(), m_solidAngleRenderpass.get()); + if (!m_visualizationPipeline) + return logFail("Could not create Graphics Pipeline!"); + + // Allocate the memory +#if DEBUG_DATA + { + constexpr size_t BufferSize = sizeof(ResultData); + + nbl::video::IGPUBuffer::SCreationParams params = {}; + params.size = BufferSize; + params.usage = IGPUBuffer::EUF_STORAGE_BUFFER_BIT | IGPUBuffer::EUF_TRANSFER_DST_BIT; + m_outputStorageBuffer = m_device->createBuffer(std::move(params)); + if (!m_outputStorageBuffer) + logFail("Failed to create a GPU Buffer of size %d!\n", params.size); + + m_outputStorageBuffer->setObjectDebugName("ResultData output buffer"); + + nbl::video::IDeviceMemoryBacked::SDeviceMemoryRequirements reqs = m_outputStorageBuffer->getMemoryReqs(); + reqs.memoryTypeBits &= m_physicalDevice->getHostVisibleMemoryTypeBits(); + + m_allocation = m_device->allocate(reqs, m_outputStorageBuffer.get(), nbl::video::IDeviceMemoryAllocation::EMAF_NONE); + if (!m_allocation.isValid()) + logFail("Failed to allocate Device Memory compatible with our GPU Buffer!\n"); + + assert(m_outputStorageBuffer->getBoundMemory().memory == m_allocation.memory.get()); + smart_refctd_ptr pool = m_device->createDescriptorPoolForDSLayouts(IDescriptorPool::ECF_NONE, { &dsLayout.get(),1 }); + + m_ds = pool->createDescriptorSet(std::move(dsLayout)); + { + IGPUDescriptorSet::SDescriptorInfo info[1]; + info[0].desc = smart_refctd_ptr(m_outputStorageBuffer); + info[0].info.buffer = { .offset = 0,.size = BufferSize }; + IGPUDescriptorSet::SWriteDescriptorSet writes[1] = { + {.dstSet = m_ds.get(),.binding = 0,.arrayElement = 0,.count = 1,.info = info} + }; + m_device->updateDescriptorSets(writes, {}); + } + } + + if (!m_allocation.memory->map({ 0ull,m_allocation.memory->getAllocationSize() }, IDeviceMemoryAllocation::EMCAF_READ)) + logFail("Failed to map the Device Memory!\n"); + + // if the mapping is not coherent the range needs to be invalidated to pull in new data for the CPU's caches + const ILogicalDevice::MappedMemoryRange memoryRange(m_allocation.memory.get(), 0ull, m_allocation.memory->getAllocationSize()); + if (!m_allocation.memory->getMemoryPropertyFlags().hasFlags(IDeviceMemoryAllocation::EMPF_HOST_COHERENT_BIT)) + m_device->invalidateMappedMemoryRanges(1, &memoryRange); +#endif + } + + // Create ImGUI + { + auto scRes = static_cast(m_surface->getSwapchainResources()); + ext::imgui::UI::SCreationParameters params = {}; + params.resources.texturesInfo = { .setIx = 0u,.bindingIx = TexturesImGUIBindingIndex }; + params.resources.samplersInfo = { .setIx = 0u,.bindingIx = 1u }; + params.utilities = m_utils; + params.transfer = getTransferUpQueue(); + params.pipelineLayout = ext::imgui::UI::createDefaultPipelineLayout(m_utils->getLogicalDevice(), params.resources.texturesInfo, params.resources.samplersInfo, MaxImGUITextures); + params.assetManager = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); + params.renderpass = smart_refctd_ptr(scRes->getRenderpass()); + params.subpassIx = 0u; + params.pipelineCache = nullptr; + interface.imGUI = ext::imgui::UI::create(std::move(params)); + if (!interface.imGUI) + return logFail("Failed to create `nbl::ext::imgui::UI` class"); + } + + // create rest of User Interface + { + auto* imgui = interface.imGUI.get(); + // create the suballocated descriptor set + { + // note that we use default layout provided by our extension, but you are free to create your own by filling ext::imgui::UI::S_CREATION_PARAMETERS::resources + const auto* layout = interface.imGUI->getPipeline()->getLayout()->getDescriptorSetLayout(0u); + auto pool = m_device->createDescriptorPoolForDSLayouts(IDescriptorPool::E_CREATE_FLAGS::ECF_UPDATE_AFTER_BIND_BIT, { &layout,1 }); + auto ds = pool->createDescriptorSet(smart_refctd_ptr(layout)); + interface.subAllocDS = make_smart_refctd_ptr(std::move(ds)); + if (!interface.subAllocDS) + return logFail("Failed to create the descriptor set"); + // make sure Texture Atlas slot is taken for eternity + { + auto dummy = SubAllocatedDescriptorSet::invalid_value; + interface.subAllocDS->multi_allocate(0, 1, &dummy); + assert(dummy == ext::imgui::UI::FontAtlasTexId); + } + // write constant descriptors, note we don't create info & write pair for the samplers because UI extension's are immutable and baked into DS layout + IGPUDescriptorSet::SDescriptorInfo info = {}; + info.desc = smart_refctd_ptr(interface.imGUI->getFontAtlasView()); + info.info.image.imageLayout = IImage::LAYOUT::READ_ONLY_OPTIMAL; + const IGPUDescriptorSet::SWriteDescriptorSet write = { + .dstSet = interface.subAllocDS->getDescriptorSet(), + .binding = TexturesImGUIBindingIndex, + .arrayElement = ext::imgui::UI::FontAtlasTexId, + .count = 1, + .info = &info + }; + if (!m_device->updateDescriptorSets({ &write,1 }, {})) + return logFail("Failed to write the descriptor set"); + } + imgui->registerListener([this]() {interface(); }); + } + + interface.camera.mapKeysToWASD(); + + onAppInitializedFinish(); + return true; + } + + // + virtual inline bool onAppTerminated() + { + SubAllocatedDescriptorSet::value_type fontAtlasDescIx = ext::imgui::UI::FontAtlasTexId; + IGPUDescriptorSet::SDropDescriptorSet dummy[1]; + interface.subAllocDS->multi_deallocate(dummy, TexturesImGUIBindingIndex, 1, &fontAtlasDescIx); + return device_base_t::onAppTerminated(); + } + + inline IQueue::SSubmitInfo::SSemaphoreInfo renderFrame(const std::chrono::microseconds nextPresentationTimestamp) override + { + // CPU events + update(nextPresentationTimestamp); + + { + const auto& virtualSolidAngleWindowRes = interface.solidAngleViewTransformReturnInfo.sceneResolution; + const auto& virtualMainWindowRes = interface.mainViewTransformReturnInfo.sceneResolution; + if (!m_solidAngleViewFramebuffer || m_solidAngleViewFramebuffer->getCreationParameters().width != virtualSolidAngleWindowRes[0] || m_solidAngleViewFramebuffer->getCreationParameters().height != virtualSolidAngleWindowRes[1] || + !m_mainViewFramebuffer || m_mainViewFramebuffer->getCreationParameters().width != virtualMainWindowRes[0] || m_mainViewFramebuffer->getCreationParameters().height != virtualMainWindowRes[1]) + recreateFramebuffers(); + } + + // + const auto resourceIx = m_realFrameIx % MaxFramesInFlight; + + auto* const cb = m_cmdBufs.data()[resourceIx].get(); + cb->reset(IGPUCommandBuffer::RESET_FLAGS::RELEASE_RESOURCES_BIT); + cb->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT); + // clear to black for both things + const IGPUCommandBuffer::SClearColorValue clearValue = { .float32 = {0.f,0.f,0.f,1.f} }; + if (m_solidAngleViewFramebuffer) + { +#if DEBUG_DATA + asset::SBufferRange range + { + .offset = 0, + .size = m_outputStorageBuffer->getSize(), + .buffer = m_outputStorageBuffer + }; + cb->fillBuffer(range, 0u); +#endif + auto creationParams = m_solidAngleViewFramebuffer->getCreationParameters(); + cb->beginDebugMarker("Draw Circle View Frame"); + { + const IGPUCommandBuffer::SClearDepthStencilValue farValue = { .depth = 0.f }; + const IGPUCommandBuffer::SRenderpassBeginInfo renderpassInfo = + { + .framebuffer = m_solidAngleViewFramebuffer.get(), + .colorClearValues = &clearValue, + .depthStencilClearValues = &farValue, + .renderArea = { + .offset = {0,0}, + .extent = {creationParams.width, creationParams.height} + } + }; + beginRenderpass(cb, renderpassInfo); + } + // draw scene + { + PushConstants pc{ + .modelMatrix = hlsl::float32_t3x4(hlsl::transpose(interface.m_OBBModelMatrix)), + .viewport = { 0.f,0.f,static_cast(creationParams.width),static_cast(creationParams.height) }, + .samplingMode = m_samplingMode, + .frameIndex = m_frameSeeding ? static_cast(m_realFrameIx) : 0u + }; + auto pipeline = m_visualizationPipeline; + cb->bindGraphicsPipeline(pipeline.get()); + cb->pushConstants(pipeline->getLayout(), hlsl::ShaderStage::ESS_FRAGMENT, 0, sizeof(PushConstants), &pc); + cb->bindDescriptorSets(nbl::asset::EPBP_GRAPHICS, pipeline->getLayout(), 0, 1, &m_ds.get()); + ext::FullScreenTriangle::recordDrawCall(cb); + } + cb->endRenderPass(); + cb->endDebugMarker(); + +#if DEBUG_DATA + m_device->waitIdle(); + std::memcpy(&m_GPUOutResulData, static_cast(m_allocation.memory->getMappedPointer()), sizeof(ResultData)); + m_device->waitIdle(); +#endif + } + // draw main view + if (m_mainViewFramebuffer) + { + cb->beginDebugMarker("Main Scene Frame"); + { + auto creationParams = m_mainViewFramebuffer->getCreationParameters(); + const IGPUCommandBuffer::SClearDepthStencilValue farValue = { .depth = 0.f }; + const IGPUCommandBuffer::SRenderpassBeginInfo renderpassInfo = + { + .framebuffer = m_mainViewFramebuffer.get(), + .colorClearValues = &clearValue, + .depthStencilClearValues = &farValue, + .renderArea = { + .offset = {0,0}, + .extent = {creationParams.width, creationParams.height} + } + }; + beginRenderpass(cb, renderpassInfo); + } + // draw scene + { + float32_t3x4 viewMatrix; + float32_t4x4 viewProjMatrix; + // TODO: get rid of legacy matrices + { + const auto& camera = interface.camera; + memcpy(&viewMatrix, camera.getViewMatrix().pointer(), sizeof(viewMatrix)); + memcpy(&viewProjMatrix, camera.getConcatenatedMatrix().pointer(), sizeof(viewProjMatrix)); + } + const auto viewParams = CSimpleDebugRenderer::SViewParams(viewMatrix, viewProjMatrix); + + // tear down scene every frame + auto& instance = m_renderer->m_instances[0]; + auto transposed = hlsl::transpose(interface.m_OBBModelMatrix); + memcpy(&instance.world, &transposed, sizeof(instance.world)); + instance.packedGeo = m_renderer->getGeometries().data(); // cube // +interface.gcIndex; + m_renderer->render(cb, viewParams); // draw the cube/OBB + + instance.world = float32_t3x4(1.0f); + instance.packedGeo = m_renderer->getGeometries().data() + 2; // disk + m_renderer->render(cb, viewParams); + } + cb->endRenderPass(); + cb->endDebugMarker(); + } + { + cb->beginDebugMarker("SolidAngleVisualizer IMGUI Frame"); + { + auto scRes = static_cast(m_surface->getSwapchainResources()); + const IGPUCommandBuffer::SRenderpassBeginInfo renderpassInfo = + { + .framebuffer = scRes->getFramebuffer(device_base_t::getCurrentAcquire().imageIndex), + .colorClearValues = &clearValue, + .depthStencilClearValues = nullptr, + .renderArea = { + .offset = {0,0}, + .extent = {m_window->getWidth(),m_window->getHeight()} + } + }; + beginRenderpass(cb, renderpassInfo); + } + // draw ImGUI + { + auto* imgui = interface.imGUI.get(); + auto* pipeline = imgui->getPipeline(); + cb->bindGraphicsPipeline(pipeline); + // note that we use default UI pipeline layout where uiParams.resources.textures.setIx == uiParams.resources.samplers.setIx + const auto* ds = interface.subAllocDS->getDescriptorSet(); + cb->bindDescriptorSets(EPBP_GRAPHICS, pipeline->getLayout(), imgui->getCreationParameters().resources.texturesInfo.setIx, 1u, &ds); + // a timepoint in the future to release streaming resources for geometry + const ISemaphore::SWaitInfo drawFinished = { .semaphore = m_semaphore.get(),.value = m_realFrameIx + 1u }; + if (!imgui->render(cb, drawFinished)) + { + m_logger->log("TODO: need to present acquired image before bailing because its already acquired.", ILogger::ELL_ERROR); + return {}; + } + } + cb->endRenderPass(); + cb->endDebugMarker(); + } + cb->end(); + + IQueue::SSubmitInfo::SSemaphoreInfo retval = + { + .semaphore = m_semaphore.get(), + .value = ++m_realFrameIx, + .stageMask = PIPELINE_STAGE_FLAGS::ALL_GRAPHICS_BITS + }; + const IQueue::SSubmitInfo::SCommandBufferInfo commandBuffers[] = + { + {.cmdbuf = cb } + }; + const IQueue::SSubmitInfo::SSemaphoreInfo acquired[] = { + { + .semaphore = device_base_t::getCurrentAcquire().semaphore, + .value = device_base_t::getCurrentAcquire().acquireCount, + .stageMask = PIPELINE_STAGE_FLAGS::NONE + } + }; + const IQueue::SSubmitInfo infos[] = + { + { + .waitSemaphores = acquired, + .commandBuffers = commandBuffers, + .signalSemaphores = {&retval,1} + } + }; + + if (getGraphicsQueue()->submit(infos) != IQueue::RESULT::SUCCESS) + { + retval.semaphore = nullptr; // so that we don't wait on semaphore that will never signal + m_realFrameIx--; + } + + + m_window->setCaption("[Nabla Engine] UI App Test Demo"); + return retval; + } + +protected: + const video::IGPURenderpass::SCreationParams::SSubpassDependency* getDefaultSubpassDependencies() const override + { + // Subsequent submits don't wait for each other, but they wait for acquire and get waited on by present + const static IGPURenderpass::SCreationParams::SSubpassDependency dependencies[] = { + // don't want any writes to be available, we'll clear, only thing to worry about is the layout transition + { + .srcSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External, + .dstSubpass = 0, + .memoryBarrier = { + .srcStageMask = PIPELINE_STAGE_FLAGS::NONE, // should sync against the semaphore wait anyway + .srcAccessMask = ACCESS_FLAGS::NONE, + // layout transition needs to finish before the color write + .dstStageMask = PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT + } + // leave view offsets and flags default + }, + // want layout transition to begin after all color output is done + { + .srcSubpass = 0, + .dstSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External, + .memoryBarrier = { + // last place where the color can get modified, depth is implicitly earlier + .srcStageMask = PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT, + // only write ops, reads can't be made available + .srcAccessMask = ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT + // spec says nothing is needed when presentation is the destination + } + // leave view offsets and flags default + }, + IGPURenderpass::SCreationParams::DependenciesEnd + }; + return dependencies; + } + +private: + inline void update(const std::chrono::microseconds nextPresentationTimestamp) + { + auto& camera = interface.camera; + camera.setMoveSpeed(interface.moveSpeed); + camera.setRotateSpeed(interface.rotateSpeed); + + m_inputSystem->getDefaultMouse(&mouse); + m_inputSystem->getDefaultKeyboard(&keyboard); + + struct + { + std::vector mouse{}; + std::vector keyboard{}; + } uiEvents; + + // TODO: should be a member really + static std::chrono::microseconds previousEventTimestamp{}; + + // I think begin/end should always be called on camera, just events shouldn't be fed, why? + // If you stop begin/end, whatever keys were up/down get their up/down values frozen leading to + // `perActionDt` becoming obnoxiously large the first time the even processing resumes due to + // `timeDiff` being computed since `lastVirtualUpTimeStamp` + camera.beginInputProcessing(nextPresentationTimestamp); + { + mouse.consumeEvents([&](const IMouseEventChannel::range_t& events) -> void + { + if (interface.move) + camera.mouseProcess(events); // don't capture the events, only let camera handle them with its impl + else + camera.mouseKeysUp(); + + for (const auto& e : events) // here capture + { + if (e.timeStamp < previousEventTimestamp) + continue; + + previousEventTimestamp = e.timeStamp; + uiEvents.mouse.emplace_back(e); + + //if (e.type == nbl::ui::SMouseEvent::EET_SCROLL && m_renderer) + //{ + // interface.gcIndex += int16_t(core::sign(e.scrollEvent.verticalScroll)); + // interface.gcIndex = core::clamp(interface.gcIndex, 0ull, m_renderer->getGeometries().size() - 1); + //} + } + }, + m_logger.get() + ); + keyboard.consumeEvents([&](const IKeyboardEventChannel::range_t& events) -> void + { + if (interface.move) + camera.keyboardProcess(events); // don't capture the events, only let camera handle them with its impl + + for (const auto& e : events) // here capture + { + if (e.timeStamp < previousEventTimestamp) + continue; + + previousEventTimestamp = e.timeStamp; + uiEvents.keyboard.emplace_back(e); + } + }, + m_logger.get() + ); + } + camera.endInputProcessing(nextPresentationTimestamp); + + const auto cursorPosition = m_window->getCursorControl()->getPosition(); + + ext::imgui::UI::SUpdateParameters params = + { + .mousePosition = float32_t2(cursorPosition.x,cursorPosition.y) - float32_t2(m_window->getX(),m_window->getY()), + .displaySize = {m_window->getWidth(),m_window->getHeight()}, + .mouseEvents = uiEvents.mouse, + .keyboardEvents = uiEvents.keyboard + }; + + //interface.objectName = m_scene->getInitParams().geometryNames[interface.gcIndex]; + interface.imGUI->update(params); + } + + void recreateFramebuffers() + { + + auto createImageAndView = [&](const uint16_t2 resolution, E_FORMAT format)->smart_refctd_ptr + { + auto image = m_device->createImage({ { + .type = IGPUImage::ET_2D, + .samples = IGPUImage::ESCF_1_BIT, + .format = format, + .extent = {resolution.x,resolution.y,1}, + .mipLevels = 1, + .arrayLayers = 1, + .usage = IGPUImage::EUF_RENDER_ATTACHMENT_BIT | IGPUImage::EUF_SAMPLED_BIT + } }); + if (!m_device->allocate(image->getMemoryReqs(), image.get()).isValid()) + return nullptr; + IGPUImageView::SCreationParams params = { + .image = std::move(image), + .viewType = IGPUImageView::ET_2D, + .format = format + }; + params.subresourceRange.aspectMask = isDepthOrStencilFormat(format) ? IGPUImage::EAF_DEPTH_BIT : IGPUImage::EAF_COLOR_BIT; + return m_device->createImageView(std::move(params)); + }; + + smart_refctd_ptr solidAngleView; + smart_refctd_ptr mainView; + const uint16_t2 solidAngleViewRes = interface.solidAngleViewTransformReturnInfo.sceneResolution; + const uint16_t2 mainViewRes = interface.mainViewTransformReturnInfo.sceneResolution; + + // detect window minimization + if (solidAngleViewRes.x < 0x4000 && solidAngleViewRes.y < 0x4000 || + mainViewRes.x < 0x4000 && mainViewRes.y < 0x4000) + { + solidAngleView = createImageAndView(solidAngleViewRes, finalSceneRenderFormat); + auto solidAngleDepthView = createImageAndView(solidAngleViewRes, sceneRenderDepthFormat); + m_solidAngleViewFramebuffer = m_device->createFramebuffer({ { + .renderpass = m_solidAngleRenderpass, + .depthStencilAttachments = &solidAngleDepthView.get(), + .colorAttachments = &solidAngleView.get(), + .width = solidAngleViewRes.x, + .height = solidAngleViewRes.y + } }); + + mainView = createImageAndView(mainViewRes, finalSceneRenderFormat); + auto mainDepthView = createImageAndView(mainViewRes, sceneRenderDepthFormat); + m_mainViewFramebuffer = m_device->createFramebuffer({ { + .renderpass = m_mainRenderpass, + .depthStencilAttachments = &mainDepthView.get(), + .colorAttachments = &mainView.get(), + .width = mainViewRes.x, + .height = mainViewRes.y + } }); + } + else + { + m_solidAngleViewFramebuffer = nullptr; + m_mainViewFramebuffer = nullptr; + } + + // release previous slot and its image + interface.subAllocDS->multi_deallocate(0, static_cast(CInterface::Count), interface.renderColorViewDescIndices, { .semaphore = m_semaphore.get(),.value = m_realFrameIx + 1 }); + // + if (solidAngleView && mainView) + { + interface.subAllocDS->multi_allocate(0, static_cast(CInterface::Count), interface.renderColorViewDescIndices); + // update descriptor set + IGPUDescriptorSet::SDescriptorInfo infos[static_cast(CInterface::Count)] = {}; + infos[0].desc = mainView; + infos[0].info.image.imageLayout = IGPUImage::LAYOUT::READ_ONLY_OPTIMAL; + infos[1].desc = solidAngleView; + infos[1].info.image.imageLayout = IGPUImage::LAYOUT::READ_ONLY_OPTIMAL; + const IGPUDescriptorSet::SWriteDescriptorSet write[static_cast(CInterface::Count)] = { + {.dstSet = interface.subAllocDS->getDescriptorSet(), + .binding = TexturesImGUIBindingIndex, + .arrayElement = interface.renderColorViewDescIndices[static_cast(CInterface::ERV_MAIN_VIEW)], + .count = 1, + .info = &infos[static_cast(CInterface::ERV_MAIN_VIEW)] + }, + { + .dstSet = interface.subAllocDS->getDescriptorSet(), + .binding = TexturesImGUIBindingIndex, + .arrayElement = interface.renderColorViewDescIndices[static_cast(CInterface::ERV_SOLID_ANGLE_VIEW)], + .count = 1, + .info = &infos[static_cast(CInterface::ERV_SOLID_ANGLE_VIEW)] + } + }; + m_device->updateDescriptorSets({ write, static_cast(CInterface::Count) }, {}); + } + interface.transformParams.sceneTexDescIx = interface.renderColorViewDescIndices[CInterface::ERV_MAIN_VIEW]; + } + + inline void beginRenderpass(IGPUCommandBuffer* cb, const IGPUCommandBuffer::SRenderpassBeginInfo& info) + { + cb->beginRenderPass(info, IGPUCommandBuffer::SUBPASS_CONTENTS::INLINE); + cb->setScissor(0, 1, &info.renderArea); + const SViewport viewport = { + .x = 0, + .y = 0, + .width = static_cast(info.renderArea.extent.width), + .height = static_cast(info.renderArea.extent.height) + }; + cb->setViewport(0u, 1u, &viewport); + } + +#if DEBUG_DATA + ~SolidAngleVisualizer() override + { + m_allocation.memory->unmap(); + } +#endif + + // Maximum frames which can be simultaneously submitted, used to cycle through our per-frame resources like command buffers + constexpr static inline uint32_t MaxFramesInFlight = 3u; + constexpr static inline auto sceneRenderDepthFormat = EF_D32_SFLOAT; + constexpr static inline auto finalSceneRenderFormat = EF_R8G8B8A8_SRGB; + constexpr static inline auto TexturesImGUIBindingIndex = 0u; + // we create the Descriptor Set with a few slots extra to spare, so we don't have to `waitIdle` the device whenever ImGUI virtual window resizes + constexpr static inline auto MaxImGUITextures = 2u + MaxFramesInFlight; + + static inline uint32_t m_samplingMode = SAMPLING_MODE_SOLID_ANGLE; + static inline bool m_frameSeeding = true; + static inline ResultData m_GPUOutResulData; + // + smart_refctd_ptr m_scene; + smart_refctd_ptr m_solidAngleRenderpass; + smart_refctd_ptr m_mainRenderpass; + smart_refctd_ptr m_renderer; + smart_refctd_ptr m_solidAngleViewFramebuffer; + smart_refctd_ptr m_mainViewFramebuffer; + smart_refctd_ptr m_visualizationPipeline; + // + nbl::video::IDeviceMemoryAllocator::SAllocation m_allocation = {}; + smart_refctd_ptr m_outputStorageBuffer; + smart_refctd_ptr m_ds = nullptr; + smart_refctd_ptr m_semaphore; + uint64_t m_realFrameIx = 0; + std::array, MaxFramesInFlight> m_cmdBufs; + // + InputSystem::ChannelReader mouse; + InputSystem::ChannelReader keyboard; + // UI stuff + struct CInterface + { + void operator()() + { + ImGuiIO& io = ImGui::GetIO(); + + // TODO: why is this a lambda and not just an assignment in a scope ? + camera.setProjectionMatrix([&]() + { + const auto& sceneRes = float16_t2(mainViewTransformReturnInfo.sceneResolution); + + matrix4SIMD projection; + if (isPerspective) + if (isLH) + projection = matrix4SIMD::buildProjectionMatrixPerspectiveFovLH(core::radians(fov), sceneRes.x / sceneRes.y, zNear, zFar); + else + projection = matrix4SIMD::buildProjectionMatrixPerspectiveFovRH(core::radians(fov), sceneRes.x / sceneRes.y, zNear, zFar); + else + { + float viewHeight = viewWidth * sceneRes.y / sceneRes.x; + + if (isLH) + projection = matrix4SIMD::buildProjectionMatrixOrthoLH(viewWidth, viewHeight, zNear, zFar); + else + projection = matrix4SIMD::buildProjectionMatrixOrthoRH(viewWidth, viewHeight, zNear, zFar); + } + + return projection; + }()); + + ImGuizmo::SetOrthographic(!isPerspective); + ImGuizmo::BeginFrame(); + + ImGui::SetNextWindowPos(ImVec2(1024, 100), ImGuiCond_Appearing); + ImGui::SetNextWindowSize(ImVec2(256, 256), ImGuiCond_Appearing); + + // create a window and insert the inspector + ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiCond_Appearing); + ImGui::SetNextWindowSize(ImVec2(320, 340), ImGuiCond_Appearing); + ImGui::Begin("Editor"); + + ImGui::Text("Sampling Mode: "); + ImGui::SameLine(); + + if (ImGui::RadioButton("Solid Angle", m_samplingMode == 0)) + m_samplingMode = SAMPLING_MODE_SOLID_ANGLE; + + ImGui::SameLine(); + + if (ImGui::RadioButton("Projected Solid Angle", m_samplingMode == 1)) + m_samplingMode = SAMPLING_MODE_PROJECTED_SOLID_ANGLE; + + ImGui::Checkbox("Frame seeding", &m_frameSeeding); + + ImGui::Separator(); + + ImGui::Text("Camera"); + + if (ImGui::RadioButton("LH", isLH)) + isLH = true; + + ImGui::SameLine(); + + if (ImGui::RadioButton("RH", !isLH)) + isLH = false; + + if (ImGui::RadioButton("Perspective", isPerspective)) + isPerspective = true; + + ImGui::SameLine(); + + if (ImGui::RadioButton("Orthographic", !isPerspective)) + isPerspective = false; + + ImGui::Checkbox("Enable \"view manipulate\"", &transformParams.enableViewManipulate); + //ImGui::Checkbox("Enable camera movement", &move); + ImGui::SliderFloat("Move speed", &moveSpeed, 0.1f, 10.f); + ImGui::SliderFloat("Rotate speed", &rotateSpeed, 0.1f, 10.f); + + // ImGui::Checkbox("Flip Gizmo's Y axis", &flipGizmoY); // let's not expose it to be changed in UI but keep the logic in case + + if (isPerspective) + ImGui::SliderFloat("Fov", &fov, 20.f, 150.f); + else + ImGui::SliderFloat("Ortho width", &viewWidth, 1, 20); + + ImGui::SliderFloat("zNear", &zNear, 0.1f, 100.f); + ImGui::SliderFloat("zFar", &zFar, 110.f, 10000.f); + + + if (firstFrame) + { + camera.setPosition(cameraIntialPosition); + camera.setTarget(cameraInitialTarget); + camera.setUpVector(cameraInitialUp); + + camera.recomputeViewMatrix(); + } + firstFrame = false; + + ImGui::Text("X: %f Y: %f", io.MousePos.x, io.MousePos.y); + if (ImGuizmo::IsUsing()) + { + ImGui::Text("Using gizmo"); + } + else + { + ImGui::Text(ImGuizmo::IsOver() ? "Over gizmo" : ""); + ImGui::SameLine(); + ImGui::Text(ImGuizmo::IsOver(ImGuizmo::TRANSLATE) ? "Over translate gizmo" : ""); + ImGui::SameLine(); + ImGui::Text(ImGuizmo::IsOver(ImGuizmo::ROTATE) ? "Over rotate gizmo" : ""); + ImGui::SameLine(); + ImGui::Text(ImGuizmo::IsOver(ImGuizmo::SCALE) ? "Over scale gizmo" : ""); + } + ImGui::Separator(); + + /* + * ImGuizmo expects view & perspective matrix to be column major both with 4x4 layout + * and Nabla uses row major matricies - 3x4 matrix for view & 4x4 for projection + + - VIEW: + + ImGuizmo + + | X[0] Y[0] Z[0] 0.0f | + | X[1] Y[1] Z[1] 0.0f | + | X[2] Y[2] Z[2] 0.0f | + | -Dot(X, eye) -Dot(Y, eye) -Dot(Z, eye) 1.0f | + + Nabla + + | X[0] X[1] X[2] -Dot(X, eye) | + | Y[0] Y[1] Y[2] -Dot(Y, eye) | + | Z[0] Z[1] Z[2] -Dot(Z, eye) | + + = transpose(nbl::core::matrix4SIMD()) + + - PERSPECTIVE [PROJECTION CASE]: + + ImGuizmo + + | (temp / temp2) (0.0) (0.0) (0.0) | + | (0.0) (temp / temp3) (0.0) (0.0) | + | ((right + left) / temp2) ((top + bottom) / temp3) ((-zfar - znear) / temp4) (-1.0f) | + | (0.0) (0.0) ((-temp * zfar) / temp4) (0.0) | + + Nabla + + | w (0.0) (0.0) (0.0) | + | (0.0) -h (0.0) (0.0) | + | (0.0) (0.0) (-zFar/(zFar-zNear)) (-zNear*zFar/(zFar-zNear)) | + | (0.0) (0.0) (-1.0) (0.0) | + + = transpose() + + * + * the ViewManipulate final call (inside EditTransform) returns world space column major matrix for an object, + * note it also modifies input view matrix but projection matrix is immutable + */ + + // No need because camera already has this functionality + // if (ImGui::IsKeyPressed(ImGuiKey_Home)) + // { + // cameraToHome(); + // } + + if (ImGui::IsKeyPressed(ImGuiKey_End)) + { + m_TRS = TRS{}; + } + + { + static struct + { + float32_t4x4 view, projection, model; + } imguizmoM16InOut; + + ImGuizmo::SetID(0u); + + // TODO: camera will return hlsl::float32_tMxN + auto view = *reinterpret_cast(camera.getViewMatrix().pointer()); + imguizmoM16InOut.view = hlsl::transpose(getMatrix3x4As4x4(view)); + + // TODO: camera will return hlsl::float32_tMxN + imguizmoM16InOut.projection = hlsl::transpose(*reinterpret_cast(camera.getProjectionMatrix().pointer())); + ImGuizmo::RecomposeMatrixFromComponents(&m_TRS.translation.x, &m_TRS.rotation.x, &m_TRS.scale.x, &imguizmoM16InOut.model[0][0]); + + if (flipGizmoY) // note we allow to flip gizmo just to match our coordinates + imguizmoM16InOut.projection[1][1] *= -1.f; // https://johannesugb.github.io/gpu-programming/why-do-opengl-proj-matrices-fail-in-vulkan/ + + transformParams.editTransformDecomposition = true; + mainViewTransformReturnInfo = EditTransform(&imguizmoM16InOut.view[0][0], &imguizmoM16InOut.projection[0][0], &imguizmoM16InOut.model[0][0], transformParams); + move = mainViewTransformReturnInfo.allowCameraMovement; + + ImGuizmo::DecomposeMatrixToComponents(&imguizmoM16InOut.model[0][0], &m_TRS.translation.x, &m_TRS.rotation.x, &m_TRS.scale.x); + ImGuizmo::RecomposeMatrixFromComponents(&m_TRS.translation.x, &m_TRS.rotation.x, &m_TRS.scale.x, &imguizmoM16InOut.model[0][0]); + } + // object meta display + //{ + // ImGui::Begin("Object"); + // ImGui::Text("type: \"%s\"", objectName.data()); + // ImGui::End(); + //} + + // solid angle view window + { + ImGui::SetNextWindowSize(ImVec2(800, 800), ImGuiCond_Appearing); + ImGui::SetNextWindowPos(ImVec2(1240, 20), ImGuiCond_Appearing); + static bool isOpen = true; + ImGui::Begin("Projected Solid Angle View", &isOpen, 0); + + ImVec2 contentRegionSize = ImGui::GetContentRegionAvail(); + solidAngleViewTransformReturnInfo.sceneResolution = uint16_t2(static_cast(contentRegionSize.x), static_cast(contentRegionSize.y)); + solidAngleViewTransformReturnInfo.allowCameraMovement = false; // not used in this view + ImGui::Image({ renderColorViewDescIndices[ERV_SOLID_ANGLE_VIEW] }, contentRegionSize); + ImGui::End(); + } + + // Show data coming from GPU +#if DEBUG_DATA + { + if (ImGui::Begin("Result Data")) + { + auto drawColorField = [&](const char* fieldName, uint32_t index) + { + ImGui::Text("%s: %u", fieldName, index); + + if (index >= 27) + { + ImGui::SameLine(); + ImGui::Text(""); + return; + } + + const auto& c = colorLUT[index]; // uses the combined LUT we made earlier + + ImGui::SameLine(); + + // Color preview button + ImGui::ColorButton( + fieldName, + ImVec4(c.r, c.g, c.b, 1.0f), + 0, + ImVec2(20, 20) + ); + + ImGui::SameLine(); + ImGui::Text("%s", colorNames[index]); + }; + + // Vertices + if (ImGui::CollapsingHeader("Vertices", ImGuiTreeNodeFlags_DefaultOpen)) + { + for (uint32_t i = 0; i < 6; ++i) + { + if (i < m_GPUOutResulData.silhouetteVertexCount) + { + ImGui::Text("corners[%u]", i); + ImGui::SameLine(); + drawColorField(":", m_GPUOutResulData.vertices[i]); + ImGui::SameLine(); + static const float32_t3 constCorners[8] = { + float32_t3(-1, -1, -1), float32_t3(1, -1, -1), float32_t3(-1, 1, -1), float32_t3(1, 1, -1), + float32_t3(-1, -1, 1), float32_t3(1, -1, 1), float32_t3(-1, 1, 1), float32_t3(1, 1, 1) + }; + float32_t3 vertexLocation = constCorners[m_GPUOutResulData.vertices[i]]; + ImGui::Text(" : (%.3f, %.3f, %.3f", vertexLocation.x, vertexLocation.y, vertexLocation.z); + } + else + { + ImGui::Text("corners[%u] :: ", i); + ImGui::SameLine(); + ImGui::ColorButton( + "", + ImVec4(0.0f, 0.0f, 0.0f, 0.0f), + 0, + ImVec2(20, 20) + ); + ImGui::SameLine(); + ImGui::Text(""); + + } + + } + } + + if (ImGui::CollapsingHeader("Color LUT Map")) + { + for (int i = 0; i < 27; i++) + drawColorField(" ", i); + } + + ImGui::Separator(); + + // Silhouette info + drawColorField("silhouetteIndex", m_GPUOutResulData.silhouetteIndex); + + ImGui::Text("silhouette Vertex Count: %u", m_GPUOutResulData.silhouetteVertexCount); + ImGui::Text("silhouette Positive VertexCount: %u", m_GPUOutResulData.positiveVertCount); + ImGui::Text("Silhouette Mismatch: %s", m_GPUOutResulData.edgeVisibilityMismatch ? "true" : "false"); + ImGui::Text("More Than Two Bit Transitions: %s", m_GPUOutResulData.maxTrianglesExcceded ? "true" : "false"); + + { + float32_t3 xAxis = m_OBBModelMatrix[0].xyz; + float32_t3 yAxis = m_OBBModelMatrix[1].xyz; + float32_t3 zAxis = m_OBBModelMatrix[2].xyz; + + float32_t3 nx = normalize(xAxis); + float32_t3 ny = normalize(yAxis); + float32_t3 nz = normalize(zAxis); + + const float epsilon = 1e-4; + bool hasSkew = false; + if (abs(dot(nx, ny)) > epsilon || abs(dot(nx, nz)) > epsilon || abs(dot(ny, nz)) > epsilon) + hasSkew = true; + ImGui::Text("Matrix Has Skew: %s", hasSkew ? "true" : "false"); + } + + static bool modalShown = false; + static uint32_t lastSilhouetteIndex = ~0u; + + // Reset modal flag if silhouette configuration changed + if (m_GPUOutResulData.silhouetteIndex != lastSilhouetteIndex) + { + modalShown = false; + lastSilhouetteIndex = m_GPUOutResulData.silhouetteIndex; + } + + if (!m_GPUOutResulData.edgeVisibilityMismatch || !m_GPUOutResulData.maxTrianglesExcceded) + { + // Reset flag when mismatch is cleared + modalShown = false; + } + if ((m_GPUOutResulData.edgeVisibilityMismatch || m_GPUOutResulData.maxTrianglesExcceded) && m_GPUOutResulData.silhouetteIndex != 13 && !modalShown) // 13 means we're inside the cube, so don't care + { + // Open modal popup only once per configuration + ImGui::OpenPopup("Edge Visibility Mismatch Warning"); + modalShown = true; + } + + // Modal popup + if (ImGui::BeginPopupModal("Edge Visibility Mismatch Warning", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "Warning: Edge Visibility Mismatch Detected!"); + ImGui::Separator(); + + ImGui::Text("The silhouette lookup table (LUT) does not match the computed edge visibility."); + ImGui::Text("This indicates the pre-computed silhouette data may be incorrect."); + ImGui::Spacing(); + + // Show configuration info + ImGui::TextWrapped("Configuration Index: %u", m_GPUOutResulData.silhouetteIndex); + ImGui::TextWrapped("Region: (%u, %u, %u)", m_GPUOutResulData.region.x, m_GPUOutResulData.region.y, m_GPUOutResulData.region.z); + ImGui::Spacing(); + + ImGui::Text("Mismatched Vertices (bitmask): 0x%08X", m_GPUOutResulData.edgeVisibilityMismatch); + + // Show which specific vertices are mismatched + ImGui::Text("Vertices involved in mismatched edges:"); + ImGui::Indent(); + for (int i = 0; i < 8; i++) + { + if (m_GPUOutResulData.edgeVisibilityMismatch & (1u << i)) + { + ImGui::BulletText("Vertex %d", i); + } + } + ImGui::Unindent(); + ImGui::Spacing(); + + if (ImGui::Button("OK", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + ImGui::Separator(); + + // Region (uint32_t3) + ImGui::Text("region: (%u, %u, %u)", + m_GPUOutResulData.region.x, m_GPUOutResulData.region.y, m_GPUOutResulData.region.z); + + ImGui::Separator(); + + // Silhouette mask printed in binary + + + auto printBin = [](uint32_t bin, const char* name) + { + char buf[33]; + for (int i = 0; i < 32; i++) + buf[i] = (bin & (1u << (31 - i))) ? '1' : '0'; + buf[32] = '\0'; + ImGui::Text("%s: 0x%08X", name, bin); + ImGui::Text("binary: 0b%s", buf); + ImGui::Separator(); + }; + printBin(m_GPUOutResulData.silhouette, "Silhouette"); + printBin(m_GPUOutResulData.rotatedSil, "rotatedSilhouette"); + + printBin(m_GPUOutResulData.clipCount, "clipCount"); + printBin(m_GPUOutResulData.clipMask, "clipMask"); + printBin(m_GPUOutResulData.rotatedClipMask, "rotatedClipMask"); + printBin(m_GPUOutResulData.rotateAmount, "rotateAmount"); + printBin(m_GPUOutResulData.wrapAround, "wrapAround"); + } + ImGui::End(); + } +#endif + // view matrices editor + { + ImGui::Begin("Matrices"); + + auto addMatrixTable = [&](const char* topText, const char* tableName, const int rows, const int columns, const float* pointer, const bool withSeparator = true) + { + ImGui::Text(topText); + if (ImGui::BeginTable(tableName, columns)) + { + for (int y = 0; y < rows; ++y) + { + ImGui::TableNextRow(); + for (int x = 0; x < columns; ++x) + { + ImGui::TableSetColumnIndex(x); + ImGui::Text("%.3f", *(pointer + (y * columns) + x)); + } + } + ImGui::EndTable(); + } + + if (withSeparator) + ImGui::Separator(); + }; + + static RandomSampler rng(69); // Initialize RNG with seed + + // Helper function to check if cube intersects unit sphere at origin + auto isCubeOutsideUnitSphere = [](const float32_t3& translation, const float32_t3& scale) -> bool { + float cubeRadius = glm::length(scale) * 0.5f; + float distanceToCenter = glm::length(translation); + return (distanceToCenter - cubeRadius) > 1.0f; + }; + + static TRS lastTRS = {}; + if (ImGui::Button("Randomize Translation")) + { + lastTRS = m_TRS; // Backup before randomizing + int attempts = 0; + do { + m_TRS.translation = float32_t3(rng.nextFloat(-3.f, 3.f), rng.nextFloat(-3.f, 3.f), rng.nextFloat(-1.f, 3.f)); + attempts++; + } while (!isCubeOutsideUnitSphere(m_TRS.translation, m_TRS.scale) && attempts < 100); + } + ImGui::SameLine(); + if (ImGui::Button("Randomize Rotation")) + { + lastTRS = m_TRS; // Backup before randomizing + m_TRS.rotation = float32_t3(rng.nextFloat(-180.f, 180.f), rng.nextFloat(-180.f, 180.f), rng.nextFloat(-180.f, 180.f)); + } + ImGui::SameLine(); + if (ImGui::Button("Randomize Scale")) + { + lastTRS = m_TRS; // Backup before randomizing + int attempts = 0; + do { + m_TRS.scale = float32_t3(rng.nextFloat(0.5f, 2.0f), rng.nextFloat(0.5f, 2.0f), rng.nextFloat(0.5f, 2.0f)); + attempts++; + } while (!isCubeOutsideUnitSphere(m_TRS.translation, m_TRS.scale) && attempts < 100); + } + //ImGui::SameLine(); + if (ImGui::Button("Randomize All")) + { + lastTRS = m_TRS; // Backup before randomizing + int attempts = 0; + do { + m_TRS.translation = float32_t3(rng.nextFloat(-3.f, 3.f), rng.nextFloat(-3.f, 3.f), rng.nextFloat(-1.f, 3.f)); + m_TRS.rotation = float32_t3(rng.nextFloat(-180.f, 180.f), rng.nextFloat(-180.f, 180.f), rng.nextFloat(-180.f, 180.f)); + m_TRS.scale = float32_t3(rng.nextFloat(0.5f, 2.0f), rng.nextFloat(0.5f, 2.0f), rng.nextFloat(0.5f, 2.0f)); + attempts++; + } while (!isCubeOutsideUnitSphere(m_TRS.translation, m_TRS.scale) && attempts < 100); + } + ImGui::SameLine(); + if (ImGui::Button("Revert to Last")) + { + m_TRS = lastTRS; // Restore backed-up TRS + } + + addMatrixTable("Model Matrix", "ModelMatrixTable", 4, 4, &m_OBBModelMatrix[0][0]); + addMatrixTable("Camera View Matrix", "ViewMatrixTable", 3, 4, camera.getViewMatrix().pointer()); + addMatrixTable("Camera View Projection Matrix", "ViewProjectionMatrixTable", 4, 4, camera.getProjectionMatrix().pointer(), false); + + ImGui::End(); + } + + // Nabla Imgui backend MDI buffer info + // To be 100% accurate and not overly conservative we'd have to explicitly `cull_frees` and defragment each time, + // so unless you do that, don't use this basic info to optimize the size of your IMGUI buffer. + { + auto* streaminingBuffer = imGUI->getStreamingBuffer(); + + const size_t total = streaminingBuffer->get_total_size(); // total memory range size for which allocation can be requested + const size_t freeSize = streaminingBuffer->getAddressAllocator().get_free_size(); // max total free bloock memory size we can still allocate from total memory available + const size_t consumedMemory = total - freeSize; // memory currently consumed by streaming buffer + + float freePercentage = 100.0f * (float)(freeSize) / (float)total; + float allocatedPercentage = (float)(consumedMemory) / (float)total; + + ImVec2 barSize = ImVec2(400, 30); + float windowPadding = 10.0f; + float verticalPadding = ImGui::GetStyle().FramePadding.y; + + ImGui::SetNextWindowSize(ImVec2(barSize.x + 2 * windowPadding, 110 + verticalPadding), ImGuiCond_Always); + ImGui::Begin("Nabla Imgui MDI Buffer Info", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar); + + ImGui::Text("Total Allocated Size: %zu bytes", total); + ImGui::Text("In use: %zu bytes", consumedMemory); + ImGui::Text("Buffer Usage:"); + + ImGui::SetCursorPosX(windowPadding); + + if (freePercentage > 70.0f) + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.0f, 1.0f, 0.0f, 0.4f)); // Green + else if (freePercentage > 30.0f) + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(1.0f, 1.0f, 0.0f, 0.4f)); // Yellow + else + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(1.0f, 0.0f, 0.0f, 0.4f)); // Red + + ImGui::ProgressBar(allocatedPercentage, barSize, ""); + + ImGui::PopStyleColor(); + + ImDrawList* drawList = ImGui::GetWindowDrawList(); + + ImVec2 progressBarPos = ImGui::GetItemRectMin(); + ImVec2 progressBarSize = ImGui::GetItemRectSize(); + + const char* text = "%.2f%% free"; + char textBuffer[64]; + snprintf(textBuffer, sizeof(textBuffer), text, freePercentage); + + ImVec2 textSize = ImGui::CalcTextSize(textBuffer); + ImVec2 textPos = ImVec2 + ( + progressBarPos.x + (progressBarSize.x - textSize.x) * 0.5f, + progressBarPos.y + (progressBarSize.y - textSize.y) * 0.5f + ); + + ImVec4 bgColor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + drawList->AddRectFilled + ( + ImVec2(textPos.x - 5, textPos.y - 2), + ImVec2(textPos.x + textSize.x + 5, textPos.y + textSize.y + 2), + ImGui::GetColorU32(bgColor) + ); + + ImGui::SetCursorScreenPos(textPos); + ImGui::Text("%s", textBuffer); + + ImGui::Dummy(ImVec2(0.0f, verticalPadding)); + + ImGui::End(); + } + ImGui::End(); + + ImGuizmo::RecomposeMatrixFromComponents(&m_TRS.translation.x, &m_TRS.rotation.x, &m_TRS.scale.x, &m_OBBModelMatrix[0][0]); + } + + smart_refctd_ptr imGUI; + + // descriptor set + smart_refctd_ptr subAllocDS; + enum E_RENDER_VIEWS : uint8_t + { + ERV_MAIN_VIEW, + ERV_SOLID_ANGLE_VIEW, + Count + }; + SubAllocatedDescriptorSet::value_type renderColorViewDescIndices[E_RENDER_VIEWS::Count] = { SubAllocatedDescriptorSet::invalid_value, SubAllocatedDescriptorSet::invalid_value }; + // + Camera camera = Camera(cameraIntialPosition, cameraInitialTarget, core::matrix4SIMD(), 1, 1, nbl::core::vectorSIMDf(0.0f, 0.0f, 1.0f)); + // mutables + struct TRS // Source of truth + { + float32_t3 translation{ 0.0f, 0.0f, 3.0f }; + float32_t3 rotation{ 0.0f }; // MUST stay orthonormal + float32_t3 scale{ 1.0f }; + } m_TRS; + float32_t4x4 m_OBBModelMatrix; // always overwritten from TRS + + //std::string_view objectName; + TransformRequestParams transformParams; + TransformReturnInfo mainViewTransformReturnInfo; + TransformReturnInfo solidAngleViewTransformReturnInfo; + + + const static inline core::vectorSIMDf cameraIntialPosition{ -3.0f, 6.0f, 3.0f }; + const static inline core::vectorSIMDf cameraInitialTarget{ 0.f, 0.0f, 3.f }; + const static inline core::vectorSIMDf cameraInitialUp{ 0.f, 0.f, 1.f }; + + float fov = 90.f, zNear = 0.1f, zFar = 10000.f, moveSpeed = 1.f, rotateSpeed = 1.f; + float viewWidth = 10.f; + //uint16_t gcIndex = {}; // note: this is dirty however since I assume only single object in scene I can leave it now, when this example is upgraded to support multiple objects this needs to be changed + bool isPerspective = true, isLH = true, flipGizmoY = true, move = true; + bool firstFrame = true; + } interface; +}; + +NBL_MAIN_FUNC(SolidAngleVisualizer) \ No newline at end of file diff --git a/73_SolidAngleVisualizer/pipeline.groovy b/73_SolidAngleVisualizer/pipeline.groovy new file mode 100644 index 000000000..7b7c9702a --- /dev/null +++ b/73_SolidAngleVisualizer/pipeline.groovy @@ -0,0 +1,50 @@ +import org.DevshGraphicsProgramming.Agent +import org.DevshGraphicsProgramming.BuilderInfo +import org.DevshGraphicsProgramming.IBuilder + +class CUIBuilder extends IBuilder +{ + public CUIBuilder(Agent _agent, _info) + { + super(_agent, _info) + } + + @Override + public boolean prepare(Map axisMapping) + { + return true + } + + @Override + public boolean build(Map axisMapping) + { + IBuilder.CONFIGURATION config = axisMapping.get("CONFIGURATION") + IBuilder.BUILD_TYPE buildType = axisMapping.get("BUILD_TYPE") + + def nameOfBuildDirectory = getNameOfBuildDirectory(buildType) + def nameOfConfig = getNameOfConfig(config) + + agent.execute("cmake --build ${info.rootProjectPath}/${nameOfBuildDirectory}/${info.targetProjectPathRelativeToRoot} --target ${info.targetBaseName} --config ${nameOfConfig} -j12 -v") + + return true + } + + @Override + public boolean test(Map axisMapping) + { + return true + } + + @Override + public boolean install(Map axisMapping) + { + return true + } +} + +def create(Agent _agent, _info) +{ + return new CUIBuilder(_agent, _info) +} + +return this \ No newline at end of file diff --git a/73_SolidAngleVisualizer/src/transform.cpp b/73_SolidAngleVisualizer/src/transform.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/CMakeLists.txt b/CMakeLists.txt index 574925e97..e17f1f701 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ if(NBL_BUILD_EXAMPLES) add_subdirectory(12_MeshLoaders) # add_subdirectory(13_MaterialCompilerTest) + add_subdirectory(14_Mortons EXCLUDE_FROM_ALL) # Waiting for a refactor #add_subdirectory(27_PLYSTLDemo) @@ -89,6 +90,8 @@ if(NBL_BUILD_EXAMPLES) add_subdirectory(70_FLIPFluids) add_subdirectory(71_RayTracingPipeline) + add_subdirectory(72_CooperativeBinarySearch) + add_subdirectory(73_SolidAngleVisualizer) # add new examples *before* NBL_GET_ALL_TARGETS invocation, it gathers recursively all targets created so far in this subdirectory NBL_GET_ALL_TARGETS(TARGETS) diff --git a/22_CppCompat/ITester.h b/common/include/nbl/examples/Tester/ITester.h similarity index 63% rename from 22_CppCompat/ITester.h rename to common/include/nbl/examples/Tester/ITester.h index 4ecd522b9..01c4973fc 100644 --- a/22_CppCompat/ITester.h +++ b/common/include/nbl/examples/Tester/ITester.h @@ -1,336 +1,402 @@ -#ifndef _NBL_EXAMPLES_TESTS_22_CPP_COMPAT_I_TESTER_INCLUDED_ -#define _NBL_EXAMPLES_TESTS_22_CPP_COMPAT_I_TESTER_INCLUDED_ - - -#include "nbl/examples/examples.hpp" - -#include "app_resources/common.hlsl" -#include "nbl/asset/metadata/CHLSLMetadata.h" - - -using namespace nbl; - -class ITester -{ -public: - virtual ~ITester() - { - m_outputBufferAllocation.memory->unmap(); - }; - - struct PipelineSetupData - { - std::string testShaderPath; - - core::smart_refctd_ptr device; - core::smart_refctd_ptr api; - core::smart_refctd_ptr assetMgr; - core::smart_refctd_ptr logger; - video::IPhysicalDevice* physicalDevice; - uint32_t computeFamilyIndex; - }; - - template - void setupPipeline(const PipelineSetupData& pipleineSetupData) - { - // setting up pipeline in the constructor - m_device = core::smart_refctd_ptr(pipleineSetupData.device); - m_physicalDevice = pipleineSetupData.physicalDevice; - m_api = core::smart_refctd_ptr(pipleineSetupData.api); - m_assetMgr = core::smart_refctd_ptr(pipleineSetupData.assetMgr); - m_logger = core::smart_refctd_ptr(pipleineSetupData.logger); - m_queueFamily = pipleineSetupData.computeFamilyIndex; - m_semaphoreCounter = 0; - m_semaphore = m_device->createSemaphore(0); - m_cmdpool = m_device->createCommandPool(m_queueFamily, video::IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT); - if (!m_cmdpool->createCommandBuffers(video::IGPUCommandPool::BUFFER_LEVEL::PRIMARY, 1u, &m_cmdbuf)) - logFail("Failed to create Command Buffers!\n"); - - // Load shaders, set up pipeline - core::smart_refctd_ptr shader; - auto shaderStage = ESS_UNKNOWN; - { - asset::IAssetLoader::SAssetLoadParams lp = {}; - lp.logger = m_logger.get(); - lp.workingDirectory = ""; // virtual root - auto assetBundle = m_assetMgr->getAsset(pipleineSetupData.testShaderPath, lp); - const auto assets = assetBundle.getContents(); - if (assets.empty() || assetBundle.getAssetType() != asset::IAsset::ET_SHADER) - { - logFail("Could not load shader!"); - assert(0); - } - - // It would be super weird if loading a shader from a file produced more than 1 asset - assert(assets.size() == 1); - core::smart_refctd_ptr source = asset::IAsset::castDown(assets[0]); - const auto hlslMetadata = static_cast(assetBundle.getMetadata()); - shaderStage = hlslMetadata->shaderStages->front(); - - auto* compilerSet = m_assetMgr->getCompilerSet(); - - asset::IShaderCompiler::SCompilerOptions options = {}; - options.stage = shaderStage; - options.preprocessorOptions.targetSpirvVersion = m_device->getPhysicalDevice()->getLimits().spirvVersion; - options.spirvOptimizer = nullptr; - options.debugInfoFlags |= asset::IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_SOURCE_BIT; - options.preprocessorOptions.sourceIdentifier = source->getFilepathHint(); - options.preprocessorOptions.logger = m_logger.get(); - options.preprocessorOptions.includeFinder = compilerSet->getShaderCompiler(source->getContentType())->getDefaultIncludeFinder(); - - shader = compilerSet->compileToSPIRV(source.get(), options); - } - - if (!shader) - logFail("Failed to create a GPU Shader, seems the Driver doesn't like the SPIR-V we're feeding it!\n"); - - video::IGPUDescriptorSetLayout::SBinding bindings[2] = { - { - .binding = 0, - .type = asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER, - .createFlags = video::IGPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, - .stageFlags = ShaderStage::ESS_COMPUTE, - .count = 1 - }, - { - .binding = 1, - .type = asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER, - .createFlags = video::IGPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, - .stageFlags = ShaderStage::ESS_COMPUTE, - .count = 1 - } - }; - - core::smart_refctd_ptr dsLayout = m_device->createDescriptorSetLayout(bindings); - if (!dsLayout) - logFail("Failed to create a Descriptor Layout!\n"); - - m_pplnLayout = m_device->createPipelineLayout({}, core::smart_refctd_ptr(dsLayout)); - if (!m_pplnLayout) - logFail("Failed to create a Pipeline Layout!\n"); - - { - video::IGPUComputePipeline::SCreationParams params = {}; - params.layout = m_pplnLayout.get(); - params.shader.entryPoint = "main"; - params.shader.shader = shader.get(); - if (!m_device->createComputePipelines(nullptr, { ¶ms,1 }, &m_pipeline)) - logFail("Failed to create pipelines (compile & link shaders)!\n"); - } - - // Allocate memory of the input buffer - { - constexpr size_t BufferSize = sizeof(InputStruct); - - video::IGPUBuffer::SCreationParams params = {}; - params.size = BufferSize; - params.usage = video::IGPUBuffer::EUF_STORAGE_BUFFER_BIT; - core::smart_refctd_ptr inputBuff = m_device->createBuffer(std::move(params)); - if (!inputBuff) - logFail("Failed to create a GPU Buffer of size %d!\n", params.size); - - inputBuff->setObjectDebugName("emulated_float64_t output buffer"); - - video::IDeviceMemoryBacked::SDeviceMemoryRequirements reqs = inputBuff->getMemoryReqs(); - reqs.memoryTypeBits &= m_physicalDevice->getHostVisibleMemoryTypeBits(); - - m_inputBufferAllocation = m_device->allocate(reqs, inputBuff.get(), video::IDeviceMemoryAllocation::EMAF_NONE); - if (!m_inputBufferAllocation.isValid()) - logFail("Failed to allocate Device Memory compatible with our GPU Buffer!\n"); - - assert(inputBuff->getBoundMemory().memory == m_inputBufferAllocation.memory.get()); - core::smart_refctd_ptr pool = m_device->createDescriptorPoolForDSLayouts(video::IDescriptorPool::ECF_NONE, { &dsLayout.get(),1 }); - - m_ds = pool->createDescriptorSet(core::smart_refctd_ptr(dsLayout)); - { - video::IGPUDescriptorSet::SDescriptorInfo info[1]; - info[0].desc = core::smart_refctd_ptr(inputBuff); - info[0].info.buffer = { .offset = 0,.size = BufferSize }; - video::IGPUDescriptorSet::SWriteDescriptorSet writes[1] = { - {.dstSet = m_ds.get(),.binding = 0,.arrayElement = 0,.count = 1,.info = info} - }; - m_device->updateDescriptorSets(writes, {}); - } - } - - // Allocate memory of the output buffer - { - constexpr size_t BufferSize = sizeof(OutputStruct); - - video::IGPUBuffer::SCreationParams params = {}; - params.size = BufferSize; - params.usage = video::IGPUBuffer::EUF_STORAGE_BUFFER_BIT; - core::smart_refctd_ptr outputBuff = m_device->createBuffer(std::move(params)); - if (!outputBuff) - logFail("Failed to create a GPU Buffer of size %d!\n", params.size); - - outputBuff->setObjectDebugName("emulated_float64_t output buffer"); - - video::IDeviceMemoryBacked::SDeviceMemoryRequirements reqs = outputBuff->getMemoryReqs(); - reqs.memoryTypeBits &= m_physicalDevice->getHostVisibleMemoryTypeBits(); - - m_outputBufferAllocation = m_device->allocate(reqs, outputBuff.get(), video::IDeviceMemoryAllocation::EMAF_NONE); - if (!m_outputBufferAllocation.isValid()) - logFail("Failed to allocate Device Memory compatible with our GPU Buffer!\n"); - - assert(outputBuff->getBoundMemory().memory == m_outputBufferAllocation.memory.get()); - core::smart_refctd_ptr pool = m_device->createDescriptorPoolForDSLayouts(video::IDescriptorPool::ECF_NONE, { &dsLayout.get(),1 }); - - { - video::IGPUDescriptorSet::SDescriptorInfo info[1]; - info[0].desc = core::smart_refctd_ptr(outputBuff); - info[0].info.buffer = { .offset = 0,.size = BufferSize }; - video::IGPUDescriptorSet::SWriteDescriptorSet writes[1] = { - {.dstSet = m_ds.get(),.binding = 1,.arrayElement = 0,.count = 1,.info = info} - }; - m_device->updateDescriptorSets(writes, {}); - } - } - - if (!m_outputBufferAllocation.memory->map({ 0ull,m_outputBufferAllocation.memory->getAllocationSize() }, video::IDeviceMemoryAllocation::EMCAF_READ)) - logFail("Failed to map the Device Memory!\n"); - - // if the mapping is not coherent the range needs to be invalidated to pull in new data for the CPU's caches - const video::ILogicalDevice::MappedMemoryRange memoryRange(m_outputBufferAllocation.memory.get(), 0ull, m_outputBufferAllocation.memory->getAllocationSize()); - if (!m_outputBufferAllocation.memory->getMemoryPropertyFlags().hasFlags(video::IDeviceMemoryAllocation::EMPF_HOST_COHERENT_BIT)) - m_device->invalidateMappedMemoryRanges(1, &memoryRange); - - assert(memoryRange.valid() && memoryRange.length >= sizeof(OutputStruct)); - - m_queue = m_device->getQueue(m_queueFamily, 0); - } - - enum class TestType - { - CPU, - GPU - }; - - template - void verifyTestValue(const std::string& memberName, const T& expectedVal, const T& testVal, const TestType testType) - { - static constexpr float MaxAllowedError = 0.1f; - if (std::abs(double(expectedVal) - double(testVal)) <= MaxAllowedError) - return; - - std::stringstream ss; - switch (testType) - { - case TestType::CPU: - ss << "CPU TEST ERROR:\n"; - case TestType::GPU: - ss << "GPU TEST ERROR:\n"; - } - - ss << "nbl::hlsl::" << memberName << " produced incorrect output! test value: " << testVal << " expected value: " << expectedVal << '\n'; - - m_logger->log(ss.str().c_str(), system::ILogger::ELL_ERROR); - } - - template - void verifyTestVector3dValue(const std::string& memberName, const nbl::hlsl::vector& expectedVal, const nbl::hlsl::vector& testVal, const TestType testType) - { - static constexpr float MaxAllowedError = 0.1f; - if (std::abs(double(expectedVal.x) - double(testVal.x)) <= MaxAllowedError && - std::abs(double(expectedVal.y) - double(testVal.y)) <= MaxAllowedError && - std::abs(double(expectedVal.z) - double(testVal.z)) <= MaxAllowedError) - return; - - std::stringstream ss; - switch (testType) - { - case TestType::CPU: - ss << "CPU TEST ERROR:\n"; - case TestType::GPU: - ss << "GPU TEST ERROR:\n"; - } - - ss << "nbl::hlsl::" << memberName << " produced incorrect output! test value: " << - testVal.x << ' ' << testVal.y << ' ' << testVal.z << - " expected value: " << expectedVal.x << ' ' << expectedVal.y << ' ' << expectedVal.z << '\n'; - - m_logger->log(ss.str().c_str(), system::ILogger::ELL_ERROR); - } - - template - void verifyTestMatrix3x3Value(const std::string& memberName, const nbl::hlsl::matrix& expectedVal, const nbl::hlsl::matrix& testVal, const TestType testType) - { - for (int i = 0; i < 3; ++i) - { - auto expectedValRow = expectedVal[i]; - auto testValRow = testVal[i]; - verifyTestVector3dValue(memberName, expectedValRow, testValRow, testType); - } - } - -protected: - uint32_t m_queueFamily; - core::smart_refctd_ptr m_device; - core::smart_refctd_ptr m_api; - video::IPhysicalDevice* m_physicalDevice; - core::smart_refctd_ptr m_assetMgr; - core::smart_refctd_ptr m_logger; - video::IDeviceMemoryAllocator::SAllocation m_inputBufferAllocation = {}; - video::IDeviceMemoryAllocator::SAllocation m_outputBufferAllocation = {}; - core::smart_refctd_ptr m_cmdbuf = nullptr; - core::smart_refctd_ptr m_cmdpool = nullptr; - core::smart_refctd_ptr m_ds = nullptr; - core::smart_refctd_ptr m_pplnLayout = nullptr; - core::smart_refctd_ptr m_pipeline; - core::smart_refctd_ptr m_semaphore; - video::IQueue* m_queue; - uint64_t m_semaphoreCounter; - - template - OutputStruct dispatch(const InputStruct& input) - { - // Update input buffer - if (!m_inputBufferAllocation.memory->map({ 0ull,m_inputBufferAllocation.memory->getAllocationSize() }, video::IDeviceMemoryAllocation::EMCAF_READ)) - logFail("Failed to map the Device Memory!\n"); - - const video::ILogicalDevice::MappedMemoryRange memoryRange(m_inputBufferAllocation.memory.get(), 0ull, m_inputBufferAllocation.memory->getAllocationSize()); - if (!m_inputBufferAllocation.memory->getMemoryPropertyFlags().hasFlags(video::IDeviceMemoryAllocation::EMPF_HOST_COHERENT_BIT)) - m_device->invalidateMappedMemoryRanges(1, &memoryRange); - - std::memcpy(static_cast(m_inputBufferAllocation.memory->getMappedPointer()), &input, sizeof(InputStruct)); - - m_inputBufferAllocation.memory->unmap(); - - // record command buffer - m_cmdbuf->reset(video::IGPUCommandBuffer::RESET_FLAGS::NONE); - m_cmdbuf->begin(video::IGPUCommandBuffer::USAGE::NONE); - m_cmdbuf->beginDebugMarker("test", core::vector4df_SIMD(0, 1, 0, 1)); - m_cmdbuf->bindComputePipeline(m_pipeline.get()); - m_cmdbuf->bindDescriptorSets(nbl::asset::EPBP_COMPUTE, m_pplnLayout.get(), 0, 1, &m_ds.get()); - m_cmdbuf->dispatch(1, 1, 1); - m_cmdbuf->endDebugMarker(); - m_cmdbuf->end(); - - video::IQueue::SSubmitInfo submitInfos[1] = {}; - const video::IQueue::SSubmitInfo::SCommandBufferInfo cmdbufs[] = { {.cmdbuf = m_cmdbuf.get()} }; - submitInfos[0].commandBuffers = cmdbufs; - const video::IQueue::SSubmitInfo::SSemaphoreInfo signals[] = { {.semaphore = m_semaphore.get(), .value = ++m_semaphoreCounter, .stageMask = asset::PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT} }; - submitInfos[0].signalSemaphores = signals; - - m_api->startCapture(); - m_queue->submit(submitInfos); - m_api->endCapture(); - - m_device->waitIdle(); - OutputStruct output; - std::memcpy(&output, static_cast(m_outputBufferAllocation.memory->getMappedPointer()), sizeof(OutputStruct)); - m_device->waitIdle(); - - return output; - } - -private: - template - inline void logFail(const char* msg, Args&&... args) - { - m_logger->log(msg, system::ILogger::ELL_ERROR, std::forward(args)...); - exit(-1); - } -}; - +#ifndef _NBL_COMMON_I_TESTER_INCLUDED_ +#define _NBL_COMMON_I_TESTER_INCLUDED_ + +#include +#include +#include +#include + +using namespace nbl; + +#include + +template +class ITester +{ +public: + struct PipelineSetupData + { + std::string shaderKey; + core::smart_refctd_ptr device; + core::smart_refctd_ptr api; + core::smart_refctd_ptr assetMgr; + core::smart_refctd_ptr logger; + video::IPhysicalDevice* physicalDevice; + uint32_t computeFamilyIndex; + }; + + void setupPipeline(const PipelineSetupData& pipleineSetupData) + { + // setting up pipeline in the constructor + m_device = core::smart_refctd_ptr(pipleineSetupData.device); + m_physicalDevice = pipleineSetupData.physicalDevice; + m_api = core::smart_refctd_ptr(pipleineSetupData.api); + m_assetMgr = core::smart_refctd_ptr(pipleineSetupData.assetMgr); + m_logger = core::smart_refctd_ptr(pipleineSetupData.logger); + m_queueFamily = pipleineSetupData.computeFamilyIndex; + m_semaphoreCounter = 0; + m_semaphore = m_device->createSemaphore(0); + m_cmdpool = m_device->createCommandPool(m_queueFamily, video::IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT); + if (!m_cmdpool->createCommandBuffers(video::IGPUCommandPool::BUFFER_LEVEL::PRIMARY, 1u, &m_cmdbuf)) + logFail("Failed to create Command Buffers!\n"); + + // Load shaders, set up pipeline + core::smart_refctd_ptr shader; + { + asset::IAssetLoader::SAssetLoadParams lp = {}; + lp.logger = m_logger.get(); + lp.workingDirectory = "app_resources"; // virtual root + auto assetBundle = m_assetMgr->getAsset(pipleineSetupData.shaderKey.data(), lp); + const auto assets = assetBundle.getContents(); + if (assets.empty()) + return logFail("Could not load shader!"); + + // It would be super weird if loading a shader from a file produced more than 1 asset + assert(assets.size() == 1); + core::smart_refctd_ptr source = asset::IAsset::castDown(assets[0]); + + shader = m_device->compileShader({ source.get() }); + } + + video::IGPUDescriptorSetLayout::SBinding bindings[2] = { + { + .binding = 0, + .type = asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER, + .createFlags = video::IGPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, + .stageFlags = ShaderStage::ESS_COMPUTE, + .count = 1 + }, + { + .binding = 1, + .type = asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER, + .createFlags = video::IGPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS::ECF_NONE, + .stageFlags = ShaderStage::ESS_COMPUTE, + .count = 1 + } + }; + + core::smart_refctd_ptr dsLayout = m_device->createDescriptorSetLayout(bindings); + if (!dsLayout) + logFail("Failed to create a Descriptor Layout!\n"); + + m_pplnLayout = m_device->createPipelineLayout({}, core::smart_refctd_ptr(dsLayout)); + if (!m_pplnLayout) + logFail("Failed to create a Pipeline Layout!\n"); + + { + video::IGPUComputePipeline::SCreationParams params = {}; + params.layout = m_pplnLayout.get(); + params.shader.entryPoint = "main"; + params.shader.shader = shader.get(); + if (!m_device->createComputePipelines(nullptr, { ¶ms,1 }, &m_pipeline)) + logFail("Failed to create pipelines (compile & link shaders)!\n"); + } + + // Allocate memory of the input buffer + { + const size_t BufferSize = sizeof(InputTestValues) * m_testIterationCount; + + video::IGPUBuffer::SCreationParams params = {}; + params.size = BufferSize; + params.usage = video::IGPUBuffer::EUF_STORAGE_BUFFER_BIT; + core::smart_refctd_ptr inputBuff = m_device->createBuffer(std::move(params)); + if (!inputBuff) + logFail("Failed to create a GPU Buffer of size %d!\n", params.size); + + inputBuff->setObjectDebugName("emulated_float64_t output buffer"); + + video::IDeviceMemoryBacked::SDeviceMemoryRequirements reqs = inputBuff->getMemoryReqs(); + reqs.memoryTypeBits &= m_physicalDevice->getHostVisibleMemoryTypeBits(); + + m_inputBufferAllocation = m_device->allocate(reqs, inputBuff.get(), video::IDeviceMemoryAllocation::EMAF_NONE); + if (!m_inputBufferAllocation.isValid()) + logFail("Failed to allocate Device Memory compatible with our GPU Buffer!\n"); + + assert(inputBuff->getBoundMemory().memory == m_inputBufferAllocation.memory.get()); + core::smart_refctd_ptr pool = m_device->createDescriptorPoolForDSLayouts(video::IDescriptorPool::ECF_NONE, { &dsLayout.get(),1 }); + + m_ds = pool->createDescriptorSet(core::smart_refctd_ptr(dsLayout)); + { + video::IGPUDescriptorSet::SDescriptorInfo info[1]; + info[0].desc = core::smart_refctd_ptr(inputBuff); + info[0].info.buffer = { .offset = 0,.size = BufferSize }; + video::IGPUDescriptorSet::SWriteDescriptorSet writes[1] = { + {.dstSet = m_ds.get(),.binding = 0,.arrayElement = 0,.count = 1,.info = info} + }; + m_device->updateDescriptorSets(writes, {}); + } + } + + // Allocate memory of the output buffer + { + const size_t BufferSize = sizeof(TestResults) * m_testIterationCount; + + video::IGPUBuffer::SCreationParams params = {}; + params.size = BufferSize; + params.usage = video::IGPUBuffer::EUF_STORAGE_BUFFER_BIT; + core::smart_refctd_ptr outputBuff = m_device->createBuffer(std::move(params)); + if (!outputBuff) + logFail("Failed to create a GPU Buffer of size %d!\n", params.size); + + outputBuff->setObjectDebugName("emulated_float64_t output buffer"); + + video::IDeviceMemoryBacked::SDeviceMemoryRequirements reqs = outputBuff->getMemoryReqs(); + reqs.memoryTypeBits &= m_physicalDevice->getHostVisibleMemoryTypeBits(); + + m_outputBufferAllocation = m_device->allocate(reqs, outputBuff.get(), video::IDeviceMemoryAllocation::EMAF_NONE); + if (!m_outputBufferAllocation.isValid()) + logFail("Failed to allocate Device Memory compatible with our GPU Buffer!\n"); + + assert(outputBuff->getBoundMemory().memory == m_outputBufferAllocation.memory.get()); + core::smart_refctd_ptr pool = m_device->createDescriptorPoolForDSLayouts(video::IDescriptorPool::ECF_NONE, { &dsLayout.get(),1 }); + + { + video::IGPUDescriptorSet::SDescriptorInfo info[1]; + info[0].desc = core::smart_refctd_ptr(outputBuff); + info[0].info.buffer = { .offset = 0,.size = BufferSize }; + video::IGPUDescriptorSet::SWriteDescriptorSet writes[1] = { + {.dstSet = m_ds.get(),.binding = 1,.arrayElement = 0,.count = 1,.info = info} + }; + m_device->updateDescriptorSets(writes, {}); + } + } + + if (!m_outputBufferAllocation.memory->map({ 0ull,m_outputBufferAllocation.memory->getAllocationSize() }, video::IDeviceMemoryAllocation::EMCAF_READ)) + logFail("Failed to map the Device Memory!\n"); + + // if the mapping is not coherent the range needs to be invalidated to pull in new data for the CPU's caches + const video::ILogicalDevice::MappedMemoryRange memoryRange(m_outputBufferAllocation.memory.get(), 0ull, m_outputBufferAllocation.memory->getAllocationSize()); + if (!m_outputBufferAllocation.memory->getMemoryPropertyFlags().hasFlags(video::IDeviceMemoryAllocation::EMPF_HOST_COHERENT_BIT)) + m_device->invalidateMappedMemoryRanges(1, &memoryRange); + + assert(memoryRange.valid() && memoryRange.length >= sizeof(TestResults)); + + m_queue = m_device->getQueue(m_queueFamily, 0); + } + + void performTestsAndVerifyResults(const std::string& logFileName) + { + m_logFile.open(logFileName, std::ios::out | std::ios::trunc); + if (!m_logFile.is_open()) + m_logger->log("Failed to open log file!", system::ILogger::ELL_ERROR); + + core::vector inputTestValues; + core::vector exceptedTestResults; + + inputTestValues.reserve(m_testIterationCount); + exceptedTestResults.reserve(m_testIterationCount); + + m_logger->log("TESTS:", system::ILogger::ELL_PERFORMANCE); + for (int i = 0; i < m_testIterationCount; ++i) + { + // Set input thest values that will be used in both CPU and GPU tests + InputTestValues testInput = generateInputTestValues(); + // use std library or glm functions to determine expected test values, the output of functions from intrinsics.hlsl will be verified against these values + TestResults expected = determineExpectedResults(testInput); + + inputTestValues.push_back(testInput); + exceptedTestResults.push_back(expected); + } + + core::vector cpuTestResults = performCpuTests(inputTestValues); + core::vector gpuTestResults = performGpuTests(inputTestValues); + + verifyAllTestResults(cpuTestResults, gpuTestResults, exceptedTestResults); + + m_logger->log("TESTS DONE.", system::ILogger::ELL_PERFORMANCE); + reloadSeed(); + + m_logFile.close(); + } + + virtual ~ITester() + { + m_outputBufferAllocation.memory->unmap(); + }; + +protected: + enum class TestType + { + CPU, + GPU + }; + + /** + * @param testBatchCount one test batch is equal to m_WorkgroupSize, so number of tests performed will be m_WorkgroupSize * testbatchCount + */ + ITester(const uint32_t testBatchCount) + : m_testBatchCount(testBatchCount), m_testIterationCount(testBatchCount * m_WorkgroupSize) + { + reloadSeed(); + }; + + virtual void verifyTestResults(const TestResults& expectedTestValues, const TestResults& testValues, const size_t testIteration, const uint32_t seed, TestType testType) = 0; + + virtual InputTestValues generateInputTestValues() = 0; + + virtual TestResults determineExpectedResults(const InputTestValues& testInput) = 0; + + std::mt19937& getRandomEngine() + { + return m_mersenneTwister; + } + +protected: + uint32_t m_queueFamily; + core::smart_refctd_ptr m_device; + core::smart_refctd_ptr m_api; + video::IPhysicalDevice* m_physicalDevice; + core::smart_refctd_ptr m_assetMgr; + core::smart_refctd_ptr m_logger; + video::IDeviceMemoryAllocator::SAllocation m_inputBufferAllocation = {}; + video::IDeviceMemoryAllocator::SAllocation m_outputBufferAllocation = {}; + core::smart_refctd_ptr m_cmdbuf = nullptr; + core::smart_refctd_ptr m_cmdpool = nullptr; + core::smart_refctd_ptr m_ds = nullptr; + core::smart_refctd_ptr m_pplnLayout = nullptr; + core::smart_refctd_ptr m_pipeline; + core::smart_refctd_ptr m_semaphore; + video::IQueue* m_queue; + uint64_t m_semaphoreCounter; + + void dispatchGpuTests(const core::vector& input, core::vector& output) + { + // Update input buffer + if (!m_inputBufferAllocation.memory->map({ 0ull,m_inputBufferAllocation.memory->getAllocationSize() }, video::IDeviceMemoryAllocation::EMCAF_READ)) + logFail("Failed to map the Device Memory!\n"); + + const video::ILogicalDevice::MappedMemoryRange memoryRange(m_inputBufferAllocation.memory.get(), 0ull, m_inputBufferAllocation.memory->getAllocationSize()); + if (!m_inputBufferAllocation.memory->getMemoryPropertyFlags().hasFlags(video::IDeviceMemoryAllocation::EMPF_HOST_COHERENT_BIT)) + m_device->invalidateMappedMemoryRanges(1, &memoryRange); + + assert(m_testIterationCount == input.size()); + const size_t inputDataSize = sizeof(InputTestValues) * m_testIterationCount; + std::memcpy(static_cast(m_inputBufferAllocation.memory->getMappedPointer()), input.data(), inputDataSize); + + m_inputBufferAllocation.memory->unmap(); + + // record command buffer + const uint32_t dispatchSizeX = m_testBatchCount; + m_cmdbuf->reset(video::IGPUCommandBuffer::RESET_FLAGS::NONE); + m_cmdbuf->begin(video::IGPUCommandBuffer::USAGE::NONE); + m_cmdbuf->beginDebugMarker("test", core::vector4df_SIMD(0, 1, 0, 1)); + m_cmdbuf->bindComputePipeline(m_pipeline.get()); + m_cmdbuf->bindDescriptorSets(nbl::asset::EPBP_COMPUTE, m_pplnLayout.get(), 0, 1, &m_ds.get()); + m_cmdbuf->dispatch(dispatchSizeX, 1, 1); + m_cmdbuf->endDebugMarker(); + m_cmdbuf->end(); + + video::IQueue::SSubmitInfo submitInfos[1] = {}; + const video::IQueue::SSubmitInfo::SCommandBufferInfo cmdbufs[] = { {.cmdbuf = m_cmdbuf.get()} }; + submitInfos[0].commandBuffers = cmdbufs; + const video::IQueue::SSubmitInfo::SSemaphoreInfo signals[] = { {.semaphore = m_semaphore.get(), .value = ++m_semaphoreCounter, .stageMask = asset::PIPELINE_STAGE_FLAGS::COMPUTE_SHADER_BIT} }; + submitInfos[0].signalSemaphores = signals; + + m_api->startCapture(); + m_queue->submit(submitInfos); + m_api->endCapture(); + + m_device->waitIdle(); + + // save test results + assert(m_testIterationCount == output.size()); + const size_t outputDataSize = sizeof(TestResults) * m_testIterationCount; + std::memcpy(output.data(), static_cast(m_outputBufferAllocation.memory->getMappedPointer()), outputDataSize); + + m_device->waitIdle(); + } + + template + void verifyTestValue(const std::string& memberName, const T& expectedVal, const T& testVal, + const size_t testIteration, const uint32_t seed, const TestType testType, const float64_t maxAllowedDifference = 0.0) + { + if (compareTestValues(expectedVal, testVal, maxAllowedDifference)) + return; + + std::stringstream ss; + switch (testType) + { + case TestType::CPU: + ss << "CPU TEST ERROR:\n"; + break; + case TestType::GPU: + ss << "GPU TEST ERROR:\n"; + } + + ss << "nbl::hlsl::" << memberName << " produced incorrect output!" << '\n'; + ss << "TEST ITERATION INDEX: " << testIteration << " SEED: " << seed << '\n'; + ss << "EXPECTED VALUE: " << system::to_string(expectedVal) << " TEST VALUE: " << system::to_string(testVal) << '\n'; + + m_logger->log("%s", system::ILogger::ELL_ERROR, ss.str().c_str()); + m_logFile << ss.str() << '\n'; + } + +private: + template + inline void logFail(const char* msg, Args&&... args) + { + m_logger->log(msg, system::ILogger::ELL_ERROR, std::forward(args)...); + exit(-1); + } + + core::vector performCpuTests(const core::vector& inputTestValues) + { + core::vector output(m_testIterationCount); + TestExecutor testExecutor; + + auto iterations = std::views::iota(0ull, m_testIterationCount); + std::for_each(std::execution::par_unseq, iterations.begin(), iterations.end(), + [&](size_t i) + { + testExecutor(inputTestValues[i], output[i]); + } + ); + + return output; + } + + core::vector performGpuTests(const core::vector& inputTestValues) + { + core::vector output(m_testIterationCount); + dispatchGpuTests(inputTestValues, output); + + return output; + } + + void verifyAllTestResults(const core::vector& cpuTestReults, const core::vector& gpuTestReults, const core::vector& exceptedTestReults) + { + for (int i = 0; i < m_testIterationCount; ++i) + { + verifyTestResults(exceptedTestReults[i], cpuTestReults[i], i, m_seed, ITester::TestType::CPU); + verifyTestResults(exceptedTestReults[i], cpuTestReults[i], i, m_seed, ITester::TestType::GPU); + } + } + + void reloadSeed() + { + std::random_device rd; + m_seed = rd(); + m_mersenneTwister = std::mt19937(m_seed); + } + + template + bool compareTestValues(const T& lhs, const T& rhs, const float64_t maxAllowedDifference) + { + return lhs == rhs; + } + template requires concepts::FloatingPointLikeScalar || concepts::FloatingPointLikeVectorial || (concepts::Matricial && concepts::FloatingPointLikeScalar::scalar_type>) + bool compareTestValues(const T& lhs, const T& rhs, const float64_t maxAllowedDifference) + { + return nbl::hlsl::testing::relativeApproxCompare(lhs, rhs, maxAllowedDifference); + } + + const size_t m_testIterationCount; + const uint32_t m_testBatchCount; + static constexpr size_t m_WorkgroupSize = 256u; + // seed will change after every call to performTestsAndVerifyResults() + std::mt19937 m_mersenneTwister; + uint32_t m_seed; + std::ofstream m_logFile; +}; + #endif \ No newline at end of file diff --git a/common/include/nbl/examples/cameras/CCamera.hpp b/common/include/nbl/examples/cameras/CCamera.hpp index 3b3cd38d8..c61f93333 100644 --- a/common/include/nbl/examples/cameras/CCamera.hpp +++ b/common/include/nbl/examples/cameras/CCamera.hpp @@ -39,6 +39,8 @@ class Camera enum E_CAMERA_MOVE_KEYS : uint8_t { ECMK_MOVE_FORWARD = 0, + ECMK_MOVE_UP, + ECMK_MOVE_DOWN, ECMK_MOVE_BACKWARD, ECMK_MOVE_LEFT, ECMK_MOVE_RIGHT, @@ -47,6 +49,8 @@ class Camera inline void mapKeysToWASD() { + keysMap[ECMK_MOVE_UP] = nbl::ui::EKC_E; + keysMap[ECMK_MOVE_DOWN] = nbl::ui::EKC_Q; keysMap[ECMK_MOVE_FORWARD] = nbl::ui::EKC_W; keysMap[ECMK_MOVE_BACKWARD] = nbl::ui::EKC_S; keysMap[ECMK_MOVE_LEFT] = nbl::ui::EKC_A; @@ -149,38 +153,36 @@ class Camera if(ev.type == nbl::ui::SMouseEvent::EET_MOVEMENT && mouseDown) { nbl::core::vectorSIMDf pos = getPosition(); - nbl::core::vectorSIMDf localTarget = getTarget() - pos; - - // Get Relative Rotation for localTarget in Radians - float relativeRotationX, relativeRotationY; - relativeRotationY = atan2(localTarget.X, localTarget.Z); - const double z1 = nbl::core::sqrt(localTarget.X*localTarget.X + localTarget.Z*localTarget.Z); - relativeRotationX = atan2(z1, localTarget.Y) - nbl::core::PI()/2; - - constexpr float RotateSpeedScale = 0.003f; - relativeRotationX -= ev.movementEvent.relativeMovementY * rotateSpeed * RotateSpeedScale * -1.0f; - float tmpYRot = ev.movementEvent.relativeMovementX * rotateSpeed * RotateSpeedScale * -1.0f; + nbl::core::vectorSIMDf upVector = getUpVector(); + nbl::core::vectorSIMDf forward = nbl::core::normalize(getTarget() - pos); + + nbl::core::vectorSIMDf right = nbl::core::normalize(nbl::core::cross(forward, upVector)); + nbl::core::vectorSIMDf up = nbl::core::normalize(nbl::core::cross(right, forward)); + + constexpr float RotateSpeedScale = 0.003f; + float pitchDelta = ev.movementEvent.relativeMovementY * rotateSpeed * RotateSpeedScale * -1.0f; + float yawDelta = ev.movementEvent.relativeMovementX * rotateSpeed * RotateSpeedScale * -1.0f; if (leftHanded) - relativeRotationY -= tmpYRot; - else - relativeRotationY += tmpYRot; + yawDelta = -yawDelta; - const double MaxVerticalAngle = nbl::core::radians(88.0f); + // Clamp pitch BEFORE applying rotation + const float MaxVerticalAngle = nbl::core::radians(88.0f); + float currentPitch = asin(nbl::core::dot(forward, upVector).X); + float newPitch = nbl::core::clamp(currentPitch + pitchDelta, -MaxVerticalAngle, MaxVerticalAngle); + pitchDelta = newPitch - currentPitch; - if (relativeRotationX > MaxVerticalAngle*2 && relativeRotationX < 2 * nbl::core::PI()-MaxVerticalAngle) - relativeRotationX = 2 * nbl::core::PI()-MaxVerticalAngle; - else - if (relativeRotationX > MaxVerticalAngle && relativeRotationX < 2 * nbl::core::PI()-MaxVerticalAngle) - relativeRotationX = MaxVerticalAngle; + // Create rotation quaternions using axis-angle method + nbl::core::quaternion pitchRot = nbl::core::quaternion::fromAngleAxis(pitchDelta, right); + nbl::core::quaternion yawRot = nbl::core::quaternion::fromAngleAxis(yawDelta, upVector); + nbl::core::quaternion combinedRot = yawRot * pitchRot; - localTarget.set(0,0, nbl::core::max(1.f, nbl::core::length(pos)[0]), 1.f); + // Apply to forward vector + forward = nbl::core::normalize(combinedRot.transformVect(forward)); - nbl::core::matrix3x4SIMD mat; - mat.setRotation(nbl::core::quaternion(relativeRotationX, relativeRotationY, 0)); - mat.transformVect(localTarget); - - setTarget(localTarget + pos); + // Set new target + float targetDistance = nbl::core::length(getTarget() - pos).X; + setTarget(pos + forward * targetDistance); } } } @@ -213,7 +215,7 @@ class Camera assert(timeDiff >= 0); // handle camera movement - for (const auto logicalKey : { ECMK_MOVE_FORWARD, ECMK_MOVE_BACKWARD, ECMK_MOVE_LEFT, ECMK_MOVE_RIGHT }) + for (const auto logicalKey : { ECMK_MOVE_FORWARD, ECMK_MOVE_UP, ECMK_MOVE_DOWN, ECMK_MOVE_BACKWARD, ECMK_MOVE_LEFT, ECMK_MOVE_RIGHT }) { const auto code = keysMap[logicalKey]; @@ -277,6 +279,9 @@ class Camera up = nbl::core::normalize(backupUpVector); } + pos += up * perActionDt[E_CAMERA_MOVE_KEYS::ECMK_MOVE_UP] * moveSpeed * MoveSpeedScale; + pos -= up * perActionDt[E_CAMERA_MOVE_KEYS::ECMK_MOVE_DOWN] * moveSpeed * MoveSpeedScale; + nbl::core::vectorSIMDf strafevect = localTarget; if (leftHanded) strafevect = nbl::core::cross(strafevect, up); @@ -297,6 +302,11 @@ class Camera lastVirtualUpTimeStamp = nextPresentationTimeStamp; } + // TODO: temporary but a good fix for the camera events when mouse stops dragging gizmo + void mouseKeysUp() + { + mouseDown = false; + } private: inline void initDefaultKeysMap() { mapKeysToWASD(); }