diff --git a/.gitignore b/.gitignore index 48a676a7..ab25f330 100644 --- a/.gitignore +++ b/.gitignore @@ -344,4 +344,7 @@ $RECYCLE.BIN/ # EXCEPTIONS !Data/Models/*/*.obj !Tools/7z.exe -!Tools/wget.exe \ No newline at end of file +!Tools/wget.exe +!Libs/WinPixEventRuntime/bin +!Libs/WinPixEventRuntime/bin/x64/ +!Libs/WinPixEventRuntime/bin/x64/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index f61f93bb..39e0fe45 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,9 @@ [submodule "Libs/D3D12MA"] path = Libs/D3D12MA url = https://github.com/vilbeyli/D3D12MemoryAllocator.git +[submodule "Libs/assimp"] + path = Libs/assimp + url = https://github.com/assimp/assimp.git +[submodule "Data/Models"] + path = Data/Models + url = https://github.com/vilbeyli/VQModels.git diff --git a/Build/GenerateProjectFiles.bat b/Build/GenerateProjectFiles.bat index d7ec8481..cf0fd25f 100644 --- a/Build/GenerateProjectFiles.bat +++ b/Build/GenerateProjectFiles.bat @@ -128,7 +128,20 @@ exit /b 0 :: :RunCmake -cmake ..\.. -G "Visual Studio 16 2019" -A x64 +:: assimp importers +set ASSIMP_IMPORT_FORMATS=-DASSIMP_BUILD_OBJ_IMPORTER=TRUE +set ASSIMP_IMPORT_FORMATS=!ASSIMP_IMPORT_FORMATS! -DASSIMP_BUILD_GLTF_IMPORTER=TRUE +:: assimp build options +set CMAKE_ASSIMP_PARAMETERS=-DASSIMP_BUILD_ASSIMP_TOOLS=OFF +set CMAKE_ASSIMP_PARAMETERS=!CMAKE_ASSIMP_PARAMETERS! -DASSIMP_NO_EXPORT=ON +set CMAKE_ASSIMP_PARAMETERS=!CMAKE_ASSIMP_PARAMETERS! -DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT=FALSE +set CMAKE_ASSIMP_PARAMETERS=!CMAKE_ASSIMP_PARAMETERS! -DBUILD_SHARED_LIBS=OFF +set CMAKE_ASSIMP_PARAMETERS=!CMAKE_ASSIMP_PARAMETERS! -DASSIMP_BUILD_TESTS=OFF +set CMAKE_ASSIMP_PARAMETERS=!CMAKE_ASSIMP_PARAMETERS! -DASSIMP_INSTALL=OFF +set CMAKE_ASSIMP_PARAMETERS=!CMAKE_ASSIMP_PARAMETERS! !ASSIMP_IMPORT_FORMATS! + + +cmake ..\.. -G "Visual Studio 16 2019" -A x64 !CMAKE_ASSIMP_PARAMETERS! if !errorlevel! EQU 0 ( echo [VQBuild] Success! @@ -140,7 +153,7 @@ if !errorlevel! EQU 0 ( echo [VQBuild] cmake VS2019 failed, retrying with VS 2017... echo [VQBuild] removing %~dp0SolutionFiles ... rmdir /S /Q %~dp0SolutionFiles - cmake ..\.. -G "Visual Studio 15 2017" -A x64 + cmake ..\.. -G "Visual Studio 15 2017" -A x64 !CMAKE_ASSIMP_PARAMETERS! if !errorlevel! NEQ 0 ( echo [VQBuild] cmake VS2017 failed, retrying without specifying VS version... echo [VQBuild] removing %~dp0SolutionFiles ... diff --git a/Build/PackageEngine.bat b/Build/PackageEngine.bat index a6b1a7b5..a92b7cba 100644 --- a/Build/PackageEngine.bat +++ b/Build/PackageEngine.bat @@ -27,9 +27,17 @@ set SKIP_DOWNLOADS=0 set DBG_BUILD_DIRECTORY=../Bin/DEBUG set RLS_BUILD_DIRECTORY=../Bin/RELEASE set RWD_BUILD_DIRECTORY=../Bin/RELWITHDEBINFO -set SHADER_DIRECTORY=../Source/Shaders +set SHADER_DIRECTORY=../Shaders set DATA_DIRECTORY=../Data +::prepare ignore directory list +pushd %cd% +cd ../Data/Icons +set IGNORE_DIRS=%cd% +cd ../Resources +set IGNORE_DIRS=%IGNORE_DIRS% %cd% +popd + :: Keep track of # build tasks. build, clean, copy/move, etc. :: assume 2 for build+copy, add more depending on prebuild+clean tasks set BUILD_NUM_TASKS=2 @@ -77,7 +85,7 @@ for %%i IN (%*) DO ( ::echo SkipMSBuildFind=!MSBUILD_FIND! if !MSBUILD_FIND! equ 1 ( call :FindMSBuild - if %ERRORLEVEL% neq 0 ( + if !ERRORLEVEL! neq 0 ( echo [VQPackage] Error: Couldn't find MSBuild exit /b -1 ) @@ -116,7 +124,7 @@ if !NO_BUILD! equ 0 ( :: Package the engine call :ExecBuildTask_Build - if %ERRORLEVEL% neq 0 exit /b %ERRORLEVEL% + if !ERRORLEVEL! neq 0 exit /b !ERRORLEVEL! ) :: move build artifacts into destination folder @@ -183,7 +191,7 @@ if not exist !SOLUTION_FILE_PATH! ( echo ********************************************************************** echo. call %~dp0GenerateProjectFiles.bat -noVS - if %ERRORLEVEL% neq 0 ( + if !ERRORLEVEL! neq 0 ( echo [VQPackage] Error: Couldn't generate project files. exit /b -1 ) @@ -210,20 +218,25 @@ exit /b 0 :ExecBuildTask_Build ::echo [VQPackage] ENGINE_BUILD_COMMAND = !ENGINE_BUILD_COMMAND! :: ---------------------- Build Release ---------------------- -call :PrintBuildStage Release -call !ENGINE_BUILD_COMMAND! /p:Configuration=Release -set /A ERR_REL=!ERRORLEVEL! -set /A BUILD_NUM_CURR_TASK=!BUILD_NUM_CURR_TASK!+1 +if !BUILD_CONFIG_RELEASE! neq 0 ( + call :PrintBuildStage Release + call !ENGINE_BUILD_COMMAND! /p:Configuration=Release + if !ERRORLEVEL! neq 0 ( + echo ERROR: BUILD ERROR + exit /b -1 + ) + set /A BUILD_NUM_CURR_TASK=!BUILD_NUM_CURR_TASK!+1 +) :: ---------------------- Build Release ---------------------- :: ---------------------- Build Debug ---------------------- if !BUILD_CONFIG_DEBUG! neq 0 ( call :PrintBuildStage Debug call !ENGINE_BUILD_COMMAND! /p:Configuration=Debug - set /A BUILD_NUM_CURR_TASK=!BUILD_NUM_CURR_TASK!+1 - if %ERRORLEVEL% neq 0 ( + if !ERRORLEVEL! neq 0 ( echo ERROR: BUILD ERROR exit /b -1 ) + set /A BUILD_NUM_CURR_TASK=!BUILD_NUM_CURR_TASK!+1 ) :: ---------------------- Build Debug ---------------------- :: ---------------------- Build RelWithDebInfo---------------- @@ -280,16 +293,17 @@ exit /b 0 :PackageBuild set SRC=%~1 set DST=%~2 -::prepare ignore directory list -pushd %cd% -cd !SRC!/Data/Icons -set IGNORE_DIRS=%cd% -cd ../Resources -set IGNORE_DIRS=%IGNORE_DIRS% %cd% -popd :: move files to final destination robocopy !SRC! !DST! /xf *.lib *.ilk /E /xd !IGNORE_DIRS! +exit /b 0 + +:: +:: PackageBuild(source, dest) +:: +:PackageResources +set DST=%~1 robocopy !SHADER_DIRECTORY! !DST!/Shaders /E +robocopy !DATA_DIRECTORY! !DST!/Data /E /xd !IGNORE_DIRS! exit /b 0 :: @@ -307,14 +321,17 @@ if exist !ENGINE_PACKAGE_OUTPUT_DIRECTORY! ( rmdir /S /Q !ENGINE_PACKAGE_OUTPUT_DIRECTORY! ) -:: make artifacts directory +:: create artifacts directory mkdir !ENGINE_PACKAGE_OUTPUT_DIRECTORY! -:: move builds +:: move data +call :PackageResources !ENGINE_PACKAGE_OUTPUT_DIRECTORY! + +:: move built binaries echo [VQPackage] Moving build artifacts to package output directory... -call :PackageBuild !RLS_BUILD_DIRECTORY!, !ENGINE_PACKAGE_OUTPUT_DIRECTORY!/Win64 -if !BUILD_CONFIG_DEBUG! NEQ 0 call :PackageBuild !DBG_BUILD_DIRECTORY!, !ENGINE_PACKAGE_OUTPUT_DIRECTORY!/Win64-Debug -if !BUILD_CONFIG_REL_WITH_DBG! NEQ 0 call :PackageBuild !RWD_BUILD_DIRECTORY!, !ENGINE_PACKAGE_OUTPUT_DIRECTORY!/Win64-PDB +call :PackageBuild !RLS_BUILD_DIRECTORY!, !ENGINE_PACKAGE_OUTPUT_DIRECTORY! +if !BUILD_CONFIG_DEBUG! NEQ 0 call :PackageBuild !DBG_BUILD_DIRECTORY!, !ENGINE_PACKAGE_OUTPUT_DIRECTORY! +if !BUILD_CONFIG_REL_WITH_DBG! NEQ 0 call :PackageBuild !RWD_BUILD_DIRECTORY!, !ENGINE_PACKAGE_OUTPUT_DIRECTORY! exit /b 0 :: -------------------------------------------------------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f3c26a2..00ce9800 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,32 +11,39 @@ add_compile_options(/MP) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}) -# ouput exe to bin directory -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_HOME_DIRECTORY}/Bin) -foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) - string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) - set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_HOME_DIRECTORY}/Bin/${OUTPUTCONFIG} ) -endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) - set (Resource "Data/Resources/VQE.rc" "Data/Resources/resource.h" "Data/Icons/VQE32.ico" ) +set (Scenes + "Data/Levels/Default.xml" + "Data/Levels/Sponza.xml" + "Data/Levels/GeometryUnitTest.xml" + "Data/Levels/StressTest.xml" + "Source/Scenes/Scenes.h" + "Source/Scenes/DefaultScene.cpp" + "Source/Scenes/SponzaScene.cpp" + "Source/Scenes/GeometryUnitTestScene.cpp" + "Source/Scenes/StressTestScene.cpp" +) + set (Config "Data/EngineSettings.ini" "Data/RendererSettings.ini" "Data/HDRDisplayProfiles.ini" "Data/EnvironmentMaps.ini" + "Data/Scenes.ini" ) set (Shaders - "Source/Shaders/hello-triangle.hlsl" - "Source/Shaders/hello-cube.hlsl" - "Source/Shaders/FullscreenTriangle.hlsl" - "Source/Shaders/Tonemapper.hlsl" - "Source/Shaders/Skydome.hlsl" + "Shaders/hello-triangle.hlsl" + "Shaders/hello-cube.hlsl" + "Shaders/FullscreenTriangle.hlsl" + "Shaders/Tonemapper.hlsl" + "Shaders/Skydome.hlsl" + "Shaders/Object.hlsl" ) set (HeaderVQE @@ -48,11 +55,19 @@ set (HeaderVQE "Source/Application/VQEngine.h" "Source/Application/Events.h" "Source/Application/Mesh.h" + "Source/Application/Material.h" + "Source/Application/Model.h" "Source/Application/Geometry.h" "Source/Application/Transform.h" "Source/Application/Quaternion.h" "Source/Application/Camera.h" "Source/Application/Input.h" + "Source/Application/AssetLoader.h" + "Source/Application/Scene.h" + "Source/Application/Light.h" + "Source/Application/GameObject.h" + "Source/Application/Memory.h" + "Source/Application/GPUMarker.h" ) set (SourceVQE @@ -64,53 +79,105 @@ set (SourceVQE "Source/Application/VQEngine_Update.cpp" "Source/Application/VQEngine_WindowEvents.cpp" "Source/Application/VQEngine_EventHandlers.cpp" + "Source/Application/FileParser.cpp" "Source/Application/Events.cpp" "Source/Application/Mesh.cpp" + "Source/Application/Material.cpp" + "Source/Application/Model.cpp" "Source/Application/Geometry.cpp" "Source/Application/Transform.cpp" "Source/Application/Math.cpp" "Source/Application/Quaternion.cpp" "Source/Application/Camera.cpp" "Source/Application/Input.cpp" + "Source/Application/AssetLoader.cpp" + "Source/Application/Scene.cpp" + "Source/Application/Light.cpp" + "Source/Application/GameObject.cpp" + "Source/Application/Memory.cpp" + "Source/Application/GPUMarker.cpp" ) -add_link_options(/SUBSYSTEM:WINDOWS) +set (PIXIncl + "Libs/WinPixEventRuntime/Include" +) +# ouput exe to bin directory +foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) + string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) + set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_HOME_DIRECTORY}/Bin/${OUTPUTCONFIG} ) +endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) + +add_link_options(/SUBSYSTEM:WINDOWS) +# add submodules add_subdirectory(Libs/VQUtils) add_subdirectory(Libs/D3D12MA) add_subdirectory(Source/Renderer) +#add_definitions( +# -DASSIMP_BUILD_ASSIMP_TOOLS=OFF +# -DASSIMP_NO_EXPORT=ON +# -DBUILD_SHARED_LIBS=OFF +# -DASSIMP_BUILD_TESTS=OFF +# -DASSIMP_INSTALL=OFF +#) +add_subdirectory(Libs/assimp) + source_group("Config" FILES ${Config}) source_group("Resource" FILES ${Resource}) source_group("Icons" FILES ${Icons}) source_group("Shaders" FILES ${Shaders}) +source_group("Scenes" FILES ${Scenes}) set_source_files_properties(${Config} PROPERTIES VS_TOOL_OVERRIDE "Text") +set_source_files_properties(${Scenes} PROPERTIES VS_TOOL_OVERRIDE "Text") set_source_files_properties(${Shaders} PROPERTIES VS_TOOL_OVERRIDE "Text") set_source_files_properties(Data/Resources/VQE.rc PROPERTIES VS_TOOL_OVERRIDE "Resource compiler") set_source_files_properties(Data/Icons/VQE32.ico PROPERTIES VS_TOOL_OVERRIDE "Image") link_directories(${CMAKE_CURRENT_SOURCE_DIR}/Libs/VQUtils/Bin/) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/Libs/WinPixEventRuntime/bin/x64) # Create a library with the project name that is build with the Headers and Source files -add_executable( ${PROJECT_NAME} ${HeaderVQE} ${SourceVQE} ${Config} ${Resource} ${Shaders} ) +add_executable( ${PROJECT_NAME} ${HeaderVQE} ${SourceVQE} ${Config} ${Scenes} ${Resource} ${Shaders} ) + +set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_HOME_DIRECTORY} ) -set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_HOME_DIRECTORY}/Bin/ ) + +# set target executable name for debug/releasewithdebug builds, release is VQE.exe +foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) + string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) + if (${OUTPUTCONFIG} MATCHES "DEBUG") + set_target_properties(${PROJECT_NAME} PROPERTIES ${OUTPUTCONFIG}_OUTPUT_NAME "VQE-d") + elseif (${OUTPUTCONFIG} MATCHES "RELWITHDEBINFO") + set_target_properties(${PROJECT_NAME} PROPERTIES ${OUTPUTCONFIG}_OUTPUT_NAME "VQE-rwd") + add_compile_definitions(PROFILE_BUILD) + endif() +endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) -add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/Data ${CMAKE_SOURCE_DIR}/Bin/Data) -add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/Source/Shaders ${CMAKE_SOURCE_DIR}/Bin/Shaders) -add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/Data $/Data) -add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/Source/Shaders $/Shaders) +set_target_properties(assimp PROPERTIES FOLDER Libs/assimp) +set_target_properties(IrrXML PROPERTIES FOLDER Libs/assimp) +set_target_properties(uninstall PROPERTIES FOLDER Libs/assimp) +set_target_properties(UpdateAssimpLibsDebugSymbolsAndDLLs PROPERTIES FOLDER Libs/assimp) +set_target_properties(zlibstatic PROPERTIES FOLDER Libs/assimp) -# TODO: the above commands must run even if the project is up to date which is not the case now. -# if Data/EngineSettings.ini is updated, the copy in the target destination of the commands above -# is not updated. +#set_target_properties(VQRenderer PROPERTIES FOLDER Libs) +#set_target_properties(VQUtils PROPERTIES FOLDER Libs) +set_target_properties(D3D12MA PROPERTIES FOLDER Libs) # Make sure the compiler can find include files for the libraries target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${Includes}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${PIXIncl}) + +target_link_libraries(${PROJECT_NAME} PRIVATE VQUtils VQRenderer assimp WinPixEventRuntime) -target_link_libraries(${PROJECT_NAME} PRIVATE VQUtils VQRenderer ) +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_CURRENT_SOURCE_DIR}/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime.dll" + $ +) diff --git a/Data/EngineSettings.ini b/Data/EngineSettings.ini index 5211887e..2a2e6ab6 100644 --- a/Data/EngineSettings.ini +++ b/Data/EngineSettings.ini @@ -6,13 +6,14 @@ RenderScale=1.0 TripleBuffer=true AntiAliasing=true MaxFrameRate=Auto -HDR=true +HDR=false [Engine] Width=768 Height=432 DisplayMode=Windowed PreferredDisplay=0 +Scene=StressTest DebugWindow=false DebugWindowWidth=450 diff --git a/Data/Levels/Default.xml b/Data/Levels/Default.xml new file mode 100644 index 00000000..133890e3 --- /dev/null +++ b/Data/Levels/Default.xml @@ -0,0 +1,84 @@ + + + + + + Stadium01 + + + + + 0.0 3.0 -5 + 15 + 0 + + Perspective + 60.0 + 0.01 + 1000 + + + 1000 + 0.05 + 9.5 + + + + 6.0 3.0 -5 + 65 + 6 + + Perspective + 90.0 + 0.01 + 5000 + + + 1000 + 0.05 + 9.5 + + + + + + Checkerboard + Procedural/Checkerboard + + + Checkerboard_Grayscale + Procedural/Checkerboard_Grayscale + + + + + + 0 0 4 + 0 0 0 1 + 3 3 3 + + + Cube + Checkerboard + + + + + + 0.0 3.0 -14 + 30 0 15 + 3.1 3.1 3.1 + + + Cube + Checkerboard_Grayscale + + + \ No newline at end of file diff --git a/Data/Levels/DefaultScene.xml b/Data/Levels/DefaultScene.xml deleted file mode 100644 index e75c1a0d..00000000 --- a/Data/Levels/DefaultScene.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - Stadium01 - - - - \ No newline at end of file diff --git a/Data/Levels/GeometryUnitTest.xml b/Data/Levels/GeometryUnitTest.xml new file mode 100644 index 00000000..6af3971b --- /dev/null +++ b/Data/Levels/GeometryUnitTest.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + GreenMat + 0 0.5 0 + 0.5 + Data/Textures/PBR/cgbookcase/black-herringbone-tiles-01/Black_herringbone_tiles_01_2K_Base_Color.png + + + RedMat + 0.8 0.0 0 + 1 + + + Checkerboard + Procedural/Checkerboard + + + Checkerboard_Grayscale + Procedural/Checkerboard_Grayscale + + + + 0.0 3.0 -5 + 15 + 0 + Perspective + 60.0 + 0.01 + 1000 + + + 1000 + 0.05 + 9.5 + + + + + + + 0.0 3.0 14 + 30 0 15 + 3.1 3.1 3.1 + + + Cube + GreenMat + + + + + + 0 0 0 + 0 0 0 1 + 1 1 1 + + + Triangle + Checkerboard + + + + + + + -5 0 0 + 0 0 0 1 + 1 1 1 + + + Triangle + Checkerboard_Grayscale + + + + + + + diff --git a/Data/Levels/Sponza.xml b/Data/Levels/Sponza.xml new file mode 100644 index 00000000..3ebe082c --- /dev/null +++ b/Data/Levels/Sponza.xml @@ -0,0 +1,79 @@ + + + + + 700.0 170.0 -50 + 15 + -90 + Perspective + 60.0 + 0.1 + 5000 + + + 5800 + 0.05 + 9.5 + + + + + + + + 0 0 0 + 0 0 0 1 + 1 1 1 + + + Data/Models/Sponza/glTF/Sponza.gltf + Sponza + + + + + + + 0 0 0 + 0 0 0 1 + 0.05 0.05 0.05 + + + Data/Models/Sponza/glTF/Sponza.gltf + Sponza + + + + + -230 0 0 + 0 0 0 1 + 0.06 0.06 0.06 + + + Data/Models/Sponza/glTF/Sponza.gltf + Sponza + + + + + 330 0 0 + 0 0 0 1 + 0.07 0.07 0.07 + + + Data/Models/Sponza/glTF/Sponza.gltf + Sponza + + + + + + + + diff --git a/Data/Levels/StressTest.xml b/Data/Levels/StressTest.xml new file mode 100644 index 00000000..bb5ea3b8 --- /dev/null +++ b/Data/Levels/StressTest.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + 0.0 24.0 -93 + 15 + 0 + Perspective + 60.0 + 0.01 + 1000 + + + 1000 + 0.05 + 9.5 + + + + + + + 40 20 0 + 0 0 0 1 + 0.02 0.02 0.02 + + + Data/Models/Sponza/glTF/Sponza.gltf + Sponza + + + + + + + -60 20 0 + 0 180 0 + 1000 1000 1000 + + + Data/Models/BoomBox/glTF/BoomBox.gltf + BoomBox + + + + + -40 20 0 + -90 0 0 + 10 10 10 + + + Data/Models/DamagedHelmet/glTF/DamagedHelmet.gltf + DamagedHelmet + + + + + -20 20 0 + 0 0 0 1 + 10 10 10 + + + Data/Models/Suzanne/glTF/Suzanne.gltf + Suzanne + + + + + + + -60 50 0 + 0 0 0 1 + 8 8 8 + + + Data/Models/SciFiHelmet/glTF/SciFiHelmet.gltf + SciFiHelmet + + + + + + -40 50 0 + 0 0 0 1 + 1 1 1 + + + Data/Models/Lantern/glTF/Lantern.gltf + Lantern + + + + + -20 40 0 + 0 0 0 1 + 50 50 50 + + + Data/Models/FlightHelmet/glTF/FlightHelmet.gltf + FlightHelmet + + + + + + diff --git a/Data/Models b/Data/Models new file mode 160000 index 00000000..942f5d87 --- /dev/null +++ b/Data/Models @@ -0,0 +1 @@ +Subproject commit 942f5d871b95683e6db8126de710994ff3f78204 diff --git a/Data/Scenes.ini b/Data/Scenes.ini new file mode 100644 index 00000000..4b060833 --- /dev/null +++ b/Data/Scenes.ini @@ -0,0 +1,5 @@ +[SceneMapping] +Default=0 +Sponza=1 +GeometryUnitTest=2 +StressTest=3 \ No newline at end of file diff --git a/Libs/VQUtils b/Libs/VQUtils index 494af9b6..07dacad7 160000 --- a/Libs/VQUtils +++ b/Libs/VQUtils @@ -1 +1 @@ -Subproject commit 494af9b63350a594f8c8b009baf89df15c1692b5 +Subproject commit 07dacad7f190fe000c33bc37f59ab5776a0ef0d8 diff --git a/Libs/WinPixEventRuntime/.signature.p7s b/Libs/WinPixEventRuntime/.signature.p7s new file mode 100644 index 00000000..ee5fe848 Binary files /dev/null and b/Libs/WinPixEventRuntime/.signature.p7s differ diff --git a/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/PIXEvents.h b/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/PIXEvents.h new file mode 100644 index 00000000..fc854f9a --- /dev/null +++ b/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/PIXEvents.h @@ -0,0 +1,531 @@ +/*==========================================================================; + * + * Copyright (C) Microsoft Corporation. All Rights Reserved. + * + * File: PIXEvents.h + * Content: PIX include file + * Don't include this file directly - use pix3.h + * + ****************************************************************************/ +#pragma once + +#ifndef _PixEvents_H_ +#define _PixEvents_H_ + +#ifndef _PIX3_H_ +# error Do not include this file directly - use pix3.h +#endif + +#include "PIXEventsCommon.h" + +#if defined(XBOX) || defined(_XBOX_ONE) || defined(_DURANGO) +# define PIX_XBOX +#endif + +#if _MSC_VER < 1800 +# error This version of pix3.h is only supported on Visual Studio 2013 or higher +#elif _MSC_VER < 1900 +# ifndef constexpr // Visual Studio 2013 doesn't support constexpr +# define constexpr +# define PIX3__DEFINED_CONSTEXPR +# endif +#endif + +namespace PIXEventsDetail +{ + template + struct PIXEventTypeInferer + { + static constexpr PIXEventType Begin() { return PIXEvent_BeginEvent_VarArgs; } + static constexpr PIXEventType SetMarker() { return PIXEvent_SetMarker_VarArgs; } + static constexpr PIXEventType BeginOnContext() { return PIXEvent_BeginEvent_OnContext_VarArgs; } + static constexpr PIXEventType SetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_VarArgs; } + + // Xbox and Windows store different types of events for context events. + // On Xbox these include a context argument, while on Windows they do + // not. It is important not to change the event types used on the + // Windows version as there are OS components (eg debug layer & DRED) + // that decode event structs. +#ifdef PIX_XBOX + static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_OnContext_VarArgs; } + static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_VarArgs; } +#else + static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_VarArgs; } + static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_VarArgs; } +#endif + }; + + template<> + struct PIXEventTypeInferer + { + static constexpr PIXEventType Begin() { return PIXEvent_BeginEvent_NoArgs; } + static constexpr PIXEventType SetMarker() { return PIXEvent_SetMarker_NoArgs; } + static constexpr PIXEventType BeginOnContext() { return PIXEvent_BeginEvent_OnContext_NoArgs; } + static constexpr PIXEventType SetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_NoArgs; } + +#ifdef PIX_XBOX + static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_OnContext_NoArgs; } + static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_NoArgs; } +#else + static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_NoArgs; } + static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_NoArgs; } +#endif + }; + + inline void PIXCopyEventArguments(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit) + { + // nothing + } + + template + void PIXCopyEventArguments(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, ARG const& arg, ARGS const&... args) + { + PIXCopyEventArgument(destination, limit, arg); + PIXCopyEventArguments(destination, limit, args...); + } + + template + __declspec(noinline) void PIXBeginEventAllocate(PIXEventsThreadInfo* threadInfo, UINT64 color, STR formatString, ARGS... args) + { + UINT64 time = PIXEventsReplaceBlock(threadInfo, false); + if (!time) + return; + + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + if (destination >= limit) + return; + + limit += PIXEventsSafeFastCopySpaceQwords; + *destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer::Begin()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, formatString, args...); + + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + + template + void PIXBeginEvent(UINT64 color, STR formatString, ARGS... args) + { + PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo(); + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + + if (destination < limit) + { + limit += PIXEventsSafeFastCopySpaceQwords; + UINT64 time = PIXGetTimestampCounter(); + *destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer::Begin()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, formatString, args...); + + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + else if (limit != nullptr) + { + PIXBeginEventAllocate(threadInfo, color, formatString); + } + } + + template + __declspec(noinline) void PIXSetMarkerAllocate(PIXEventsThreadInfo* threadInfo, UINT64 color, STR formatString, ARGS... args) + { + UINT64 time = PIXEventsReplaceBlock(threadInfo, false); + if (!time) + return; + + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + + if (destination >= limit) + return; + + limit += PIXEventsSafeFastCopySpaceQwords; + *destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer::SetMarker()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, formatString, args...); + + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + + template + void PIXSetMarker(UINT64 color, STR formatString, ARGS... args) + { + PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo(); + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + if (destination < limit) + { + limit += PIXEventsSafeFastCopySpaceQwords; + UINT64 time = PIXGetTimestampCounter(); + *destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer::SetMarker()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, formatString, args...); + + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + else if (limit != nullptr) + { + PIXSetMarkerAllocate(threadInfo, color, formatString, args...); + } + } + +#if !PIX_XBOX + template + __declspec(noinline) void PIXBeginEventOnContextCpuAllocate(PIXEventsThreadInfo* threadInfo, void* context, UINT64 color, STR formatString, ARGS... args) + { + UINT64 time = PIXEventsReplaceBlock(threadInfo, false); + if (!time) + return; + + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + + if (destination >= limit) + return; + + limit += PIXEventsSafeFastCopySpaceQwords; + *destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer::BeginOnContext()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, context, formatString, args...); + + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + + template + void PIXBeginEventOnContextCpu(void* context, UINT64 color, STR formatString, ARGS... args) + { + PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo(); + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + if (destination < limit) + { + limit += PIXEventsSafeFastCopySpaceQwords; + UINT64 time = PIXGetTimestampCounter(); + *destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer::BeginOnContext()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, context, formatString, args...); + + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + else if (limit != nullptr) + { + PIXBeginEventOnContextCpuAllocate(threadInfo, context, color, formatString, args...); + } + } +#endif + + template + void PIXBeginEvent(CONTEXT* context, UINT64 color, STR formatString, ARGS... args) + { +#if PIX_XBOX + PIXBeginEvent(color, formatString, args...); +#else + PIXBeginEventOnContextCpu(context, color, formatString, args...); +#endif + + // TODO: we've already encoded this once for the CPU event - figure out way to avoid doing it again + UINT64 buffer[PIXEventsGraphicsRecordSpaceQwords]; + UINT64* destination = buffer; + UINT64* limit = buffer + PIXEventsGraphicsRecordSpaceQwords - PIXEventsReservedTailSpaceQwords; + + *destination++ = PIXEncodeEventInfo(0, PIXEventTypeInferer::GpuBeginOnContext()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, formatString, args...); + *destination = 0ull; + + PIXBeginGPUEventOnContext(context, static_cast(buffer), static_cast(reinterpret_cast(destination) - reinterpret_cast(buffer))); + } + +#if !PIX_XBOX + template + __declspec(noinline) void PIXSetMarkerOnContextCpuAllocate(PIXEventsThreadInfo* threadInfo, void* context, UINT64 color, STR formatString, ARGS... args) + { + UINT64 time = PIXEventsReplaceBlock(threadInfo, false); + if (!time) + return; + + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + + if (destination >= limit) + return; + + limit += PIXEventsSafeFastCopySpaceQwords; + *destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer::SetMarkerOnContext()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, context, formatString, args...); + + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + + template + void PIXSetMarkerOnContextCpu(void* context, UINT64 color, STR formatString, ARGS... args) + { + PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo(); + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + if (destination < limit) + { + limit += PIXEventsSafeFastCopySpaceQwords; + UINT64 time = PIXGetTimestampCounter(); + *destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer::SetMarkerOnContext()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, context, formatString, args...); + + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + else if (limit != nullptr) + { + PIXSetMarkerOnContextCpuAllocate(threadInfo, context, color, formatString, args...); + } + } +#endif + + template + void PIXSetMarker(CONTEXT* context, UINT64 color, STR formatString, ARGS... args) + { +#if PIX_XBOX + PIXSetMarker(color, formatString, args...); +#else + PIXSetMarkerOnContextCpu(context, color, formatString, args...); +#endif + + UINT64 buffer[PIXEventsGraphicsRecordSpaceQwords]; + UINT64* destination = buffer; + UINT64* limit = buffer + PIXEventsGraphicsRecordSpaceQwords - PIXEventsReservedTailSpaceQwords; + + *destination++ = PIXEncodeEventInfo(0, PIXEventTypeInferer::GpuSetMarkerOnContext()); + *destination++ = color; + + PIXCopyEventArguments(destination, limit, formatString, args...); + *destination = 0ull; + + PIXSetGPUMarkerOnContext(context, static_cast(buffer), static_cast(reinterpret_cast(destination) - reinterpret_cast(buffer))); + } + + __declspec(noinline) inline void PIXEndEventAllocate(PIXEventsThreadInfo* threadInfo) + { + UINT64 time = PIXEventsReplaceBlock(threadInfo, true); + if (!time) + return; + + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + + if (destination >= limit) + return; + + limit += PIXEventsSafeFastCopySpaceQwords; + *destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent); + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + + inline void PIXEndEvent() + { + PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo(); + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + if (destination < limit) + { + limit += PIXEventsSafeFastCopySpaceQwords; + UINT64 time = PIXGetTimestampCounter(); + *destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent); + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + else if (limit != nullptr) + { + PIXEndEventAllocate(threadInfo); + } + } + +#if !PIX_XBOX + __declspec(noinline) inline void PIXEndEventOnContextCpuAllocate(PIXEventsThreadInfo* threadInfo, void* context) + { + UINT64 time = PIXEventsReplaceBlock(threadInfo, true); + if (!time) + return; + + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + + if (destination >= limit) + return; + + limit += PIXEventsSafeFastCopySpaceQwords; + *destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent_OnContext); + PIXCopyEventArgument(destination, limit, context); + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + + inline void PIXEndEventOnContextCpu(void* context) + { + PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo(); + UINT64* destination = threadInfo->destination; + UINT64* limit = threadInfo->biasedLimit; + if (destination < limit) + { + limit += PIXEventsSafeFastCopySpaceQwords; + UINT64 time = PIXGetTimestampCounter(); + *destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent_OnContext); + PIXCopyEventArgument(destination, limit, context); + *destination = PIXEventsBlockEndMarker; + threadInfo->destination = destination; + } + else if (limit != nullptr) + { + PIXEndEventOnContextCpuAllocate(threadInfo, context); + } + } +#endif + + template + void PIXEndEvent(CONTEXT* context) + { +#if PIX_XBOX + PIXEndEvent(); +#else + PIXEndEventOnContextCpu(context); +#endif + PIXEndGPUEventOnContext(context); + } + +} + +template +void PIXBeginEvent(UINT64 color, PCWSTR formatString, ARGS... args) +{ + PIXEventsDetail::PIXBeginEvent(color, formatString, args...); +} + +template +void PIXBeginEvent(UINT64 color, PCSTR formatString, ARGS... args) +{ + PIXEventsDetail::PIXBeginEvent(color, formatString, args...); +} + +template +void PIXSetMarker(UINT64 color, PCWSTR formatString, ARGS... args) +{ + PIXEventsDetail::PIXSetMarker(color, formatString, args...); +} + +template +void PIXSetMarker(UINT64 color, PCSTR formatString, ARGS... args) +{ + PIXEventsDetail::PIXSetMarker(color, formatString, args...); +} + +template +void PIXBeginEvent(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args) +{ + PIXEventsDetail::PIXBeginEvent(context, color, formatString, args...); +} + +template +void PIXBeginEvent(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args) +{ + PIXEventsDetail::PIXBeginEvent(context, color, formatString, args...); +} + +template +void PIXSetMarker(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args) +{ + PIXEventsDetail::PIXSetMarker(context, color, formatString, args...); +} + +template +void PIXSetMarker(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args) +{ + PIXEventsDetail::PIXSetMarker(context, color, formatString, args...); +} + +inline void PIXEndEvent() +{ + PIXEventsDetail::PIXEndEvent(); +} + +template +void PIXEndEvent(CONTEXT* context) +{ + PIXEventsDetail::PIXEndEvent(context); +} + +template +class PIXScopedEventObject +{ + CONTEXT* m_context; + +public: + template + PIXScopedEventObject(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args) + : m_context(context) + { + PIXBeginEvent(context, color, formatString, args...); + } + + template + PIXScopedEventObject(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args) + : m_context(context) + { + PIXBeginEvent(context, color, formatString, args...); + } + + ~PIXScopedEventObject() + { + PIXEndEvent(m_context); + } +}; + +template<> +class PIXScopedEventObject +{ +public: + template + PIXScopedEventObject(UINT64 color, PCWSTR formatString, ARGS... args) + { + PIXBeginEvent(color, formatString, args...); + } + + template + PIXScopedEventObject(UINT64 color, PCSTR formatString, ARGS... args) + { + PIXBeginEvent(color, formatString, args...); + } + + ~PIXScopedEventObject() + { + PIXEndEvent(); + } +}; + +#define PIXConcatenate(a, b) a ## b +#define PIXGetScopedEventVariableName(a, b) PIXConcatenate(a, b) +#define PIXScopedEvent(context, ...) PIXScopedEventObject::Type> PIXGetScopedEventVariableName(pixEvent, __LINE__)(context, __VA_ARGS__) + +#ifdef PIX3__DEFINED_CONSTEXPR +#undef constexpr +#undef PIX3__DEFINED_CONSTEXPR +#endif + +#endif // _PIXEvents_H__ diff --git a/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/PIXEventsCommon.h b/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/PIXEventsCommon.h new file mode 100644 index 00000000..a2fac07d --- /dev/null +++ b/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/PIXEventsCommon.h @@ -0,0 +1,587 @@ +/*==========================================================================; +* +* Copyright (C) Microsoft Corporation. All Rights Reserved. +* +* File: PIXEventsCommon.h +* Content: PIX include file +* Don't include this file directly - use pix3.h +* +****************************************************************************/ +#pragma once + +#ifndef _PIXEventsCommon_H_ +#define _PIXEventsCommon_H_ + +#include + +#if defined(_M_X64) || defined(_M_IX86) +#include +#endif + +// +// The PIXBeginEvent and PIXSetMarker functions have an optimized path for +// copying strings that work by copying 128-bit or 64-bits at a time. In some +// circumstances this may result in PIX logging the remaining memory after the +// null terminator. +// +// By default this optimization is enabled unless Address Sanitizer is enabled, +// since this optimization can trigger a global-buffer-overflow when copying +// string literals. +// +// The PIX_ENABLE_BLOCK_ARGUMENT_COPY controls whether or not this optimization +// is enabled. Applications may also explicitly set this macro to 0 to disable +// the optimization if necessary. +// + +#if defined(PIX_ENABLE_BLOCK_ARGUMENT_COPY) +// Previously set values override everything +# define PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET 0 +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +// Disable block argument copy when address sanitizer is enabled +# define PIX_ENABLE_BLOCK_ARGUMENT_COPY 0 +# define PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET 1 +# endif +#endif + +#if !defined(PIX_ENABLE_BLOCK_ARGUMENT_COPY) +// Default to enabled. +# define PIX_ENABLE_BLOCK_ARGUMENT_COPY 1 +# define PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET 1 +#endif + +struct PIXEventsBlockInfo; + +struct PIXEventsThreadInfo +{ + PIXEventsBlockInfo* block; + UINT64* biasedLimit; + UINT64* destination; +}; + +extern "C" UINT64 WINAPI PIXEventsReplaceBlock(PIXEventsThreadInfo* threadInfo, bool getEarliestTime) noexcept; + +enum PIXEventType +{ + PIXEvent_EndEvent = 0x000, + PIXEvent_BeginEvent_VarArgs = 0x001, + PIXEvent_BeginEvent_NoArgs = 0x002, + PIXEvent_SetMarker_VarArgs = 0x007, + PIXEvent_SetMarker_NoArgs = 0x008, + + PIXEvent_EndEvent_OnContext = 0x010, + PIXEvent_BeginEvent_OnContext_VarArgs = 0x011, + PIXEvent_BeginEvent_OnContext_NoArgs = 0x012, + PIXEvent_SetMarker_OnContext_VarArgs = 0x017, + PIXEvent_SetMarker_OnContext_NoArgs = 0x018, +}; + +static const UINT64 PIXEventsReservedRecordSpaceQwords = 64; +//this is used to make sure SSE string copy always will end 16-byte write in the current block +//this way only a check if destination < limit can be performed, instead of destination < limit - 1 +//since both these are UINT64* and SSE writes in 16 byte chunks, 8 bytes are kept in reserve +//so even if SSE overwrites 8 extra bytes, those will still belong to the correct block +//on next iteration check destination will be greater than limit +//this is used as well for fixed size UMD events and PIXEndEvent since these require less space +//than other variable length user events and do not need big reserved space +static const UINT64 PIXEventsReservedTailSpaceQwords = 2; +static const UINT64 PIXEventsSafeFastCopySpaceQwords = PIXEventsReservedRecordSpaceQwords - PIXEventsReservedTailSpaceQwords; +static const UINT64 PIXEventsGraphicsRecordSpaceQwords = 64; + +//Bits 7-19 (13 bits) +static const UINT64 PIXEventsBlockEndMarker = 0x00000000000FFF80; + +//Bits 10-19 (10 bits) +static const UINT64 PIXEventsTypeReadMask = 0x00000000000FFC00; +static const UINT64 PIXEventsTypeWriteMask = 0x00000000000003FF; +static const UINT64 PIXEventsTypeBitShift = 10; + +//Bits 20-63 (44 bits) +static const UINT64 PIXEventsTimestampReadMask = 0xFFFFFFFFFFF00000; +static const UINT64 PIXEventsTimestampWriteMask = 0x00000FFFFFFFFFFF; +static const UINT64 PIXEventsTimestampBitShift = 20; + +inline UINT64 PIXEncodeEventInfo(UINT64 timestamp, PIXEventType eventType) +{ + return ((timestamp & PIXEventsTimestampWriteMask) << PIXEventsTimestampBitShift) | + (((UINT64)eventType & PIXEventsTypeWriteMask) << PIXEventsTypeBitShift); +} + +//Bits 60-63 (4) +static const UINT64 PIXEventsStringAlignmentWriteMask = 0x000000000000000F; +static const UINT64 PIXEventsStringAlignmentReadMask = 0xF000000000000000; +static const UINT64 PIXEventsStringAlignmentBitShift = 60; + +//Bits 55-59 (5) +static const UINT64 PIXEventsStringCopyChunkSizeWriteMask = 0x000000000000001F; +static const UINT64 PIXEventsStringCopyChunkSizeReadMask = 0x0F80000000000000; +static const UINT64 PIXEventsStringCopyChunkSizeBitShift = 55; + +//Bit 54 +static const UINT64 PIXEventsStringIsANSIWriteMask = 0x0000000000000001; +static const UINT64 PIXEventsStringIsANSIReadMask = 0x0040000000000000; +static const UINT64 PIXEventsStringIsANSIBitShift = 54; + +//Bit 53 +static const UINT64 PIXEventsStringIsShortcutWriteMask = 0x0000000000000001; +static const UINT64 PIXEventsStringIsShortcutReadMask = 0x0020000000000000; +static const UINT64 PIXEventsStringIsShortcutBitShift = 53; + +inline UINT64 PIXEncodeStringInfo(UINT64 alignment, UINT64 copyChunkSize, BOOL isANSI, BOOL isShortcut) +{ + return ((alignment & PIXEventsStringAlignmentWriteMask) << PIXEventsStringAlignmentBitShift) | + ((copyChunkSize & PIXEventsStringCopyChunkSizeWriteMask) << PIXEventsStringCopyChunkSizeBitShift) | + (((UINT64)isANSI & PIXEventsStringIsANSIWriteMask) << PIXEventsStringIsANSIBitShift) | + (((UINT64)isShortcut & PIXEventsStringIsShortcutWriteMask) << PIXEventsStringIsShortcutBitShift); +} + +template +inline bool PIXIsPointerAligned(T* pointer) +{ + return !(((UINT64)pointer) & (alignment - 1)); +} + +// Generic template version slower because of the additional clear write +template +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, T argument) +{ + if (destination < limit) + { + *destination = 0ull; + *((T*)destination) = argument; + ++destination; + } +} + +// int32 specialization to avoid slower double memory writes +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, INT32 argument) +{ + if (destination < limit) + { + *reinterpret_cast(destination) = static_cast(argument); + ++destination; + } +} + +// unsigned int32 specialization to avoid slower double memory writes +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, UINT32 argument) +{ + if (destination < limit) + { + *destination = static_cast(argument); + ++destination; + } +} + +// int64 specialization to avoid slower double memory writes +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, INT64 argument) +{ + if (destination < limit) + { + *reinterpret_cast(destination) = argument; + ++destination; + } +} + +// unsigned int64 specialization to avoid slower double memory writes +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, UINT64 argument) +{ + if (destination < limit) + { + *destination = argument; + ++destination; + } +} + +//floats must be cast to double during writing the data to be properly printed later when reading the data +//this is needed because when float is passed to varargs function it's cast to double +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, float argument) +{ + if (destination < limit) + { + *reinterpret_cast(destination) = static_cast(argument); + ++destination; + } +} + +//char has to be cast to a longer signed integer type +//this is due to printf not ignoring correctly the upper bits of unsigned long long for a char format specifier +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, char argument) +{ + if (destination < limit) + { + *reinterpret_cast(destination) = static_cast(argument); + ++destination; + } +} + +//unsigned char has to be cast to a longer unsigned integer type +//this is due to printf not ignoring correctly the upper bits of unsigned long long for a char format specifier +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, unsigned char argument) +{ + if (destination < limit) + { + *destination = static_cast(argument); + ++destination; + } +} + +//bool has to be cast to an integer since it's not explicitly supported by string format routines +//there's no format specifier for bool type, but it should work with integer format specifiers +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, bool argument) +{ + if (destination < limit) + { + *destination = static_cast(argument); + ++destination; + } +} + +inline void PIXCopyEventArgumentSlowest(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCSTR argument) +{ + *destination++ = PIXEncodeStringInfo(0, 8, TRUE, FALSE); + while (destination < limit) + { + UINT64 c = static_cast(argument[0]); + if (!c) + { + *destination++ = 0; + return; + } + UINT64 x = c; + c = static_cast(argument[1]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 8; + c = static_cast(argument[2]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 16; + c = static_cast(argument[3]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 24; + c = static_cast(argument[4]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 32; + c = static_cast(argument[5]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 40; + c = static_cast(argument[6]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 48; + c = static_cast(argument[7]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 56; + *destination++ = x; + argument += 8; + } +} + +inline void PIXCopyEventArgumentSlow(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCSTR argument) +{ +#if PIX_ENABLE_BLOCK_ARGUMENT_COPY + if (PIXIsPointerAligned<8>(argument)) + { + *destination++ = PIXEncodeStringInfo(0, 8, TRUE, FALSE); + UINT64* source = (UINT64*)argument; + while (destination < limit) + { + UINT64 qword = *source++; + *destination++ = qword; + //check if any of the characters is a terminating zero + if (!((qword & 0xFF00000000000000) && + (qword & 0xFF000000000000) && + (qword & 0xFF0000000000) && + (qword & 0xFF00000000) && + (qword & 0xFF000000) && + (qword & 0xFF0000) && + (qword & 0xFF00) && + (qword & 0xFF))) + { + break; + } + } + } + else +#endif // PIX_ENABLE_BLOCK_ARGUMENT_COPY + { + PIXCopyEventArgumentSlowest(destination, limit, argument); + } +} + +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCSTR argument) +{ + if (destination < limit) + { + if (argument != nullptr) + { +#if (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY + if (PIXIsPointerAligned<16>(argument)) + { + *destination++ = PIXEncodeStringInfo(0, 16, TRUE, FALSE); + __m128i zero = _mm_setzero_si128(); + if (PIXIsPointerAligned<16>(destination)) + { + while (destination < limit) + { + __m128i mem = _mm_load_si128((__m128i*)argument); + _mm_store_si128((__m128i*)destination, mem); + //check if any of the characters is a terminating zero + __m128i res = _mm_cmpeq_epi8(mem, zero); + destination += 2; + if (_mm_movemask_epi8(res)) + break; + argument += 16; + } + } + else + { + while (destination < limit) + { + __m128i mem = _mm_load_si128((__m128i*)argument); + _mm_storeu_si128((__m128i*)destination, mem); + //check if any of the characters is a terminating zero + __m128i res = _mm_cmpeq_epi8(mem, zero); + destination += 2; + if (_mm_movemask_epi8(res)) + break; + argument += 16; + } + } + } + else +#endif // (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY + { + PIXCopyEventArgumentSlow(destination, limit, argument); + } + } + else + { + *destination++ = 0ull; + } + } +} + +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PSTR argument) +{ + PIXCopyEventArgument(destination, limit, (PCSTR)argument); +} + +inline void PIXCopyEventArgumentSlowest(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCWSTR argument) +{ + *destination++ = PIXEncodeStringInfo(0, 8, FALSE, FALSE); + while (destination < limit) + { + UINT64 c = static_cast(argument[0]); + if (!c) + { + *destination++ = 0; + return; + } + UINT64 x = c; + c = static_cast(argument[1]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 16; + c = static_cast(argument[2]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 32; + c = static_cast(argument[3]); + if (!c) + { + *destination++ = x; + return; + } + x |= c << 48; + *destination++ = x; + argument += 4; + } +} + +inline void PIXCopyEventArgumentSlow(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCWSTR argument) +{ +#if PIX_ENABLE_BLOCK_ARGUMENT_COPY + if (PIXIsPointerAligned<8>(argument)) + { + *destination++ = PIXEncodeStringInfo(0, 8, FALSE, FALSE); + UINT64* source = (UINT64*)argument; + while (destination < limit) + { + UINT64 qword = *source++; + *destination++ = qword; + //check if any of the characters is a terminating zero + //TODO: check if reversed condition is faster + if (!((qword & 0xFFFF000000000000) && + (qword & 0xFFFF00000000) && + (qword & 0xFFFF0000) && + (qword & 0xFFFF))) + { + break; + } + } + } + else +#endif // PIX_ENABLE_BLOCK_ARGUMENT_COPY + { + PIXCopyEventArgumentSlowest(destination, limit, argument); + } +} + +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCWSTR argument) +{ + if (destination < limit) + { + if (argument != nullptr) + { +#if (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY + if (PIXIsPointerAligned<16>(argument)) + { + *destination++ = PIXEncodeStringInfo(0, 16, FALSE, FALSE); + __m128i zero = _mm_setzero_si128(); + if (PIXIsPointerAligned<16>(destination)) + { + while (destination < limit) + { + __m128i mem = _mm_load_si128((__m128i*)argument); + _mm_store_si128((__m128i*)destination, mem); + //check if any of the characters is a terminating zero + __m128i res = _mm_cmpeq_epi16(mem, zero); + destination += 2; + if (_mm_movemask_epi8(res)) + break; + argument += 8; + } + } + else + { + while (destination < limit) + { + __m128i mem = _mm_load_si128((__m128i*)argument); + _mm_storeu_si128((__m128i*)destination, mem); + //check if any of the characters is a terminating zero + __m128i res = _mm_cmpeq_epi16(mem, zero); + destination += 2; + if (_mm_movemask_epi8(res)) + break; + argument += 8; + } + } + } + else +#endif // (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY + { + PIXCopyEventArgumentSlow(destination, limit, argument); + } + } + else + { + *destination++ = 0ull; + } + } +} + +template<> +inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PWSTR argument) +{ + PIXCopyEventArgument(destination, limit, (PCWSTR)argument); +}; + +#if defined(__d3d12_x_h__) || defined(__d3d12_h__) + +inline void PIXSetGPUMarkerOnContext(_In_ ID3D12GraphicsCommandList* commandList, _In_reads_bytes_(size) void* data, UINT size) +{ + commandList->SetMarker(D3D12_EVENT_METADATA, data, size); +} + +inline void PIXSetGPUMarkerOnContext(_In_ ID3D12CommandQueue* commandQueue, _In_reads_bytes_(size) void* data, UINT size) +{ + commandQueue->SetMarker(D3D12_EVENT_METADATA, data, size); +} + +inline void PIXBeginGPUEventOnContext(_In_ ID3D12GraphicsCommandList* commandList, _In_reads_bytes_(size) void* data, UINT size) +{ + commandList->BeginEvent(D3D12_EVENT_METADATA, data, size); +} + +inline void PIXBeginGPUEventOnContext(_In_ ID3D12CommandQueue* commandQueue, _In_reads_bytes_(size) void* data, UINT size) +{ + commandQueue->BeginEvent(D3D12_EVENT_METADATA, data, size); +} + +inline void PIXEndGPUEventOnContext(_In_ ID3D12GraphicsCommandList* commandList) +{ + commandList->EndEvent(); +} + +inline void PIXEndGPUEventOnContext(_In_ ID3D12CommandQueue* commandQueue) +{ + commandQueue->EndEvent(); +} + +#endif //__d3d12_x_h__ + +template struct PIXInferScopedEventType { typedef T Type; }; +template struct PIXInferScopedEventType { typedef T Type; }; +template struct PIXInferScopedEventType { typedef T Type; }; +template struct PIXInferScopedEventType { typedef T Type; }; +template<> struct PIXInferScopedEventType { typedef void Type; }; +template<> struct PIXInferScopedEventType { typedef void Type; }; +template<> struct PIXInferScopedEventType { typedef void Type; }; +template<> struct PIXInferScopedEventType { typedef void Type; }; +template<> struct PIXInferScopedEventType { typedef void Type; }; +template<> struct PIXInferScopedEventType { typedef void Type; }; +template<> struct PIXInferScopedEventType { typedef void Type; }; +template<> struct PIXInferScopedEventType { typedef void Type; }; + + +#if PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET +#undef PIX_ENABLE_BLOCK_ARGUMENT_COPY +#endif + +#undef PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET + +#endif //_PIXEventsCommon_H_ diff --git a/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/pix3.h b/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/pix3.h new file mode 100644 index 00000000..1b73fc96 --- /dev/null +++ b/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/pix3.h @@ -0,0 +1,144 @@ +/*==========================================================================; + * + * Copyright (C) Microsoft Corporation. All Rights Reserved. + * + * File: pix3.h + * Content: PIX include file + * + ****************************************************************************/ +#pragma once + +#ifndef _PIX3_H_ +#define _PIX3_H_ + +#include + +#ifndef __cplusplus +#error "Only C++ files can include pix3.h. C is not supported." +#endif + +#if !defined(USE_PIX_SUPPORTED_ARCHITECTURE) +#if defined(_M_X64) || defined(USE_PIX_ON_ALL_ARCHITECTURES) || defined(_M_ARM64) +#define USE_PIX_SUPPORTED_ARCHITECTURE +#endif +#endif + +#if !defined(USE_PIX) +#if defined(USE_PIX_SUPPORTED_ARCHITECTURE) && (defined(_DEBUG) || DBG || defined(PROFILE) || defined(PROFILE_BUILD)) && !defined(_PREFAST_) +#define USE_PIX +#endif +#endif + +#if defined(USE_PIX) && !defined(USE_PIX_SUPPORTED_ARCHITECTURE) +#pragma message("Warning: Pix markers are only supported on AMD64 and ARM64") +#endif + +#if defined(XBOX) || defined(_XBOX_ONE) || defined(_DURANGO) || defined(_GAMING_XBOX) +#include "pix3_xbox.h" +#else +#include "pix3_win.h" +#endif + +// These flags are used by both PIXBeginCapture and PIXGetCaptureState +#define PIX_CAPTURE_TIMING (1 << 0) +#define PIX_CAPTURE_GPU (1 << 1) +#define PIX_CAPTURE_FUNCTION_SUMMARY (1 << 2) +#define PIX_CAPTURE_FUNCTION_DETAILS (1 << 3) +#define PIX_CAPTURE_CALLGRAPH (1 << 4) +#define PIX_CAPTURE_INSTRUCTION_TRACE (1 << 5) +#define PIX_CAPTURE_SYSTEM_MONITOR_COUNTERS (1 << 6) +#define PIX_CAPTURE_VIDEO (1 << 7) +#define PIX_CAPTURE_AUDIO (1 << 8) + +union PIXCaptureParameters +{ + enum PIXCaptureStorage + { + Hybrid = 0, + Disk, + Memory, + }; + + struct GpuCaptureParameters + { + PVOID reserved; + } GpuCaptureParameters; + + struct TimingCaptureParameters + { + PWSTR FileName; + UINT32 MaximumToolingMemorySizeMb; + PIXCaptureStorage CaptureStorage; + + BOOL CaptureGpuTiming; + + BOOL CaptureCallstacks; + BOOL CaptureCpuSamples; + UINT32 CpuSamplesPerSecond; + } TimingCaptureParameters; +}; + +typedef PIXCaptureParameters* PPIXCaptureParameters; + + +#if defined(USE_PIX) && defined(USE_PIX_SUPPORTED_ARCHITECTURE) + +#define PIX_EVENTS_ARE_TURNED_ON + +#include "PIXEventsCommon.h" +#include "PIXEvents.h" + +// Starts a programmatically controlled capture. +// captureFlags uses the PIX_CAPTURE_* family of flags to specify the type of capture to take +extern "C" HRESULT WINAPI PIXBeginCapture1(DWORD captureFlags, _In_opt_ const PPIXCaptureParameters captureParameters); +inline HRESULT PIXBeginCapture(DWORD captureFlags, _In_opt_ const PPIXCaptureParameters captureParameters) { return PIXBeginCapture1(captureFlags, captureParameters); } + +// Stops a programmatically controlled capture +// If discard == TRUE, the captured data is discarded +// If discard == FALSE, the captured data is saved +extern "C" HRESULT WINAPI PIXEndCapture(BOOL discard); + +extern "C" DWORD WINAPI PIXGetCaptureState(); + +extern "C" void WINAPI PIXReportCounter(_In_ PCWSTR name, float value); + +#else + +// Eliminate these APIs when not using PIX +inline HRESULT PIXBeginCapture1(DWORD, _In_opt_ const PIXCaptureParameters*) { return S_OK; } +inline HRESULT PIXBeginCapture(DWORD, _In_opt_ const PIXCaptureParameters*) { return S_OK; } +inline HRESULT PIXEndCapture(BOOL) { return S_OK; } +inline DWORD PIXGetCaptureState() { return 0; } +inline void PIXReportCounter(_In_ PCWSTR, float) {} +inline void PIXNotifyWakeFromFenceSignal(_In_ HANDLE) {} + +inline void PIXBeginEvent(UINT64, _In_ PCSTR, ...) {} +inline void PIXBeginEvent(UINT64, _In_ PCWSTR, ...) {} +inline void PIXBeginEvent(void*, UINT64, _In_ PCSTR, ...) {} +inline void PIXBeginEvent(void*, UINT64, _In_ PCWSTR, ...) {} +inline void PIXEndEvent() {} +inline void PIXEndEvent(void*) {} +inline void PIXSetMarker(UINT64, _In_ PCSTR, ...) {} +inline void PIXSetMarker(UINT64, _In_ PCWSTR, ...) {} +inline void PIXSetMarker(void*, UINT64, _In_ PCSTR, ...) {} +inline void PIXSetMarker(void*, UINT64, _In_ PCWSTR, ...) {} +inline void PIXScopedEvent(UINT64, _In_ PCSTR, ...) {} +inline void PIXScopedEvent(UINT64, _In_ PCWSTR, ...) {} +inline void PIXScopedEvent(void*, UINT64, _In_ PCSTR, ...) {} +inline void PIXScopedEvent(void*, UINT64, _In_ PCWSTR, ...) {} + +// don't show warnings about expressions with no effect +#pragma warning(disable:4548) +#pragma warning(disable:4555) + +#endif // USE_PIX + +// Use these functions to specify colors to pass as metadata to a PIX event/marker API. +// Use PIX_COLOR() to specify a particular color for an event. +// Or, use PIX_COLOR_INDEX() to specify a set of unique event categories, and let PIX choose +// the colors to represent each category. +inline UINT PIX_COLOR(BYTE r, BYTE g, BYTE b) { return 0xff000000 | (r << 16) | (g << 8) | b; } +inline UINT PIX_COLOR_INDEX(BYTE i) { return i; } +const UINT PIX_COLOR_DEFAULT = PIX_COLOR_INDEX(0); + +#endif // _PIX3_H_ diff --git a/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/pix3_win.h b/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/pix3_win.h new file mode 100644 index 00000000..7b2b8462 --- /dev/null +++ b/Libs/WinPixEventRuntime/Include/WinPixEventRuntime/pix3_win.h @@ -0,0 +1,48 @@ +/*==========================================================================; + * + * Copyright (C) Microsoft Corporation. All Rights Reserved. + * + * File: PIX3_win.h + * Content: PIX include file + * Don't include this file directly - use pix3.h + * + ****************************************************************************/ + +#pragma once + +#ifndef _PIX3_H_ +#error Don't include this file directly - use pix3.h +#endif + +#ifndef _PIX3_WIN_H_ +#define _PIX3_WIN_H_ + + // PIXEventsThreadInfo is defined in PIXEventsCommon.h +struct PIXEventsThreadInfo; + +extern "C" PIXEventsThreadInfo* PIXGetThreadInfo() noexcept; + +#if defined(USE_PIX) && defined(USE_PIX_SUPPORTED_ARCHITECTURE) +// Notifies PIX that an event handle was set as a result of a D3D12 fence being signaled. +// The event specified must have the same handle value as the handle +// used in ID3D12Fence::SetEventOnCompletion. +extern "C" void WINAPI PIXNotifyWakeFromFenceSignal(_In_ HANDLE event); +#endif + +// The following defines denote the different metadata values that have been used +// by tools to denote how to parse pix marker event data. The first two values +// are legacy values. +#define WINPIX_EVENT_UNICODE_VERSION 0 +#define WINPIX_EVENT_ANSI_VERSION 1 +#define WINPIX_EVENT_PIX3BLOB_VERSION 2 + +#define D3D12_EVENT_METADATA WINPIX_EVENT_PIX3BLOB_VERSION + +__forceinline UINT64 PIXGetTimestampCounter() +{ + LARGE_INTEGER time = {}; + QueryPerformanceCounter(&time); + return time.QuadPart; +} + +#endif //_PIX3_WIN_H_ diff --git a/Libs/WinPixEventRuntime/ThirdPartyNotices.txt b/Libs/WinPixEventRuntime/ThirdPartyNotices.txt new file mode 100644 index 00000000..8281e7e5 --- /dev/null +++ b/Libs/WinPixEventRuntime/ThirdPartyNotices.txt @@ -0,0 +1,149 @@ +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION + +Note: While Microsoft is not the author of the files below, Microsoft is +offering you a license subject to the terms of the Microsoft Software License +Terms for Microsoft PIX Developer Tool (the "Microsoft Program"). Microsoft +reserves all other rights. The notices below are provided for informational +purposes only and are not the license terms under which Microsoft distributes +these files. + +The Microsoft Program includes the following third-party software: + +1. Boost v. 1.66.0 (https://sourceforge.net/projects/boost/files/boost/1.66.0) +2. fmt v4.1.0 (https://github.com/fmtlib/fmt/releases/tag/4.1.0) + + +As the recipient of the above third-party software, Microsoft sets forth a copy +of the notices and other information below. + + +BOOST NOTICES AND INFORMATION BEGIN HERE +======================================== + +Boost v. 1.66.0 +Copyright Beman Dawes, David Abrahams, 1998-2005 +Copyright Rene Rivera 2006-2007 +Provided for Informational Purposes Only +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by this +license (the "Software") to use, reproduce, display, distribute, execute, and +transmit the Software, and to prepare derivative works of the Software, and to +permit third-parties to whom the Software is furnished to do so, all subject to +the following: + +The copyright notices in the Software and this entire statement, including the +above license grant, this restriction and the following disclaimer, must be +included in all copies of the Software, in whole or in part, and all derivative +works of the Software, unless such copies or derivative works are solely in the +form of machine-executable object code generated by a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL +THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY +DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE + +END OF BOOST NOTICES AND INFORMATION +==================================== + +FMT NOTICES AND INFORMATION BEGIN HERE +====================================== + +fmt v4.1.0 +Copyright (c) 2012 - 2016, Victor Zverovich +Provided for Informational Purposes Only +BSD 2-clause + + +Copyright (c) 2012 - 2016, Victor Zverovich + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +END OF FMT NOTICES AND INFORMATION +================================== + +RETROFIT NOTICES AND INFORMATION BEGIN HERE +=========================================== + +Copyright (C) 2016 Max Delta Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE MAX +DELTA GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the Max Delta Group and its +members shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization from the +Max Delta Group. + +END OF RETROFIT NOTICES AND INFORMATION +======================================= + +SQLITE3 NOTICES AND INFORMATION BEGIN HERE +========================================== + +https://www.sqlite.org/copyright.html +SQLite Is Public Domain + +All of the code and documentation in SQLite has been dedicated to the public +domain by the authors. All code authors, and representatives of the companies +they work for, have signed affidavits dedicating their contributions to the +public domain and originals of those signed affidavits are stored in a firesafe +at the main offices of Hwaci. Anyone is free to copy, modify, publish, use, +compile, sell, or distribute the original SQLite code, either in source code +form or as a compiled binary, for any purpose, commercial or non-commercial, and +by any means. + +The previous paragraph applies to the deliverable code and documentation in SQLite - +those parts of the SQLite library that you actually bundle and ship with a larger +application. Some scripts used as part of the build process (for example the "configure" +scripts generated by autoconf) might fall under other open-source licenses. Nothing +from these build scripts ever reaches the final deliverable SQLite library, however, +and so the licenses associated with those scripts should not be a factor in assessing +your rights to copy and use the SQLite library. + +All of the deliverable code in SQLite has been written from scratch. No code has been +taken from other projects or from the open internet. Every line of code can be traced +back to its original author, and all of those authors have public domain dedications +on file. So the SQLite code base is clean and is uncontaminated with licensed code +from other projects. + +END OF SQLITE3 NOTICES AND INFORMATION +====================================== diff --git a/Libs/WinPixEventRuntime/WinPixEventRuntime.nuspec b/Libs/WinPixEventRuntime/WinPixEventRuntime.nuspec new file mode 100644 index 00000000..17c51db1 --- /dev/null +++ b/Libs/WinPixEventRuntime/WinPixEventRuntime.nuspec @@ -0,0 +1,18 @@ + + + + WinPixEventRuntime + 1.0.200127001 + WinPixEventRuntime + Microsoft + pix,microsoft + https://www.microsoft.com/web/webpi/eula/eula_pix_event_runtime.htm + https://devblogs.microsoft.com/pix/winpixeventruntime/ + http://www.gravatar.com/avatar/df362936893270e6a1b53807fa90e8fa + false + Allows applications to be instrumented with marker events, for use with Microsoft PIX. + https://devblogs.microsoft.com/pix/winpixeventruntime/ + © Microsoft Corporation. All rights reserved. + PIX Direct3D graphics 3D + + \ No newline at end of file diff --git a/Libs/WinPixEventRuntime/[Content_Types].xml b/Libs/WinPixEventRuntime/[Content_Types].xml new file mode 100644 index 00000000..ee5b747c --- /dev/null +++ b/Libs/WinPixEventRuntime/[Content_Types].xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/WinPixEventRuntime/_rels/.rels b/Libs/WinPixEventRuntime/_rels/.rels new file mode 100644 index 00000000..73f9b819 --- /dev/null +++ b/Libs/WinPixEventRuntime/_rels/.rels @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime.dll b/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime.dll new file mode 100644 index 00000000..41f36bbd Binary files /dev/null and b/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime.dll differ diff --git a/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime.lib b/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime.lib new file mode 100644 index 00000000..7b2e619a Binary files /dev/null and b/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime.lib differ diff --git a/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime_UAP.dll b/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime_UAP.dll new file mode 100644 index 00000000..36e74556 Binary files /dev/null and b/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime_UAP.dll differ diff --git a/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime_UAP.lib b/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime_UAP.lib new file mode 100644 index 00000000..0592a02b Binary files /dev/null and b/Libs/WinPixEventRuntime/bin/x64/WinPixEventRuntime_UAP.lib differ diff --git a/Libs/WinPixEventRuntime/build/WinPixEventRuntime.targets b/Libs/WinPixEventRuntime/build/WinPixEventRuntime.targets new file mode 100644 index 00000000..3679cf9d --- /dev/null +++ b/Libs/WinPixEventRuntime/build/WinPixEventRuntime.targets @@ -0,0 +1,68 @@ + + + + + + + $(MSBuildThisFileDirectory)..\Include\WinPixEventRuntime;%(AdditionalIncludeDirectories) + + + + + + $(MSBuildThisFileDirectory)..\bin\x64;%(AdditionalLibraryDirectories) + WinPixEventRuntime_UAP.lib;%(AdditionalDependencies) + + + + + + $(MSBuildThisFileDirectory)..\bin\ARM64;%(AdditionalLibraryDirectories) + WinPixEventRuntime_UAP.lib;%(AdditionalDependencies) + + + + + + WinPixEventBinary + $(ProjectName) + %(Filename)%(Extension) + + + + + + WinPixEventBinary + $(ProjectName) + %(Filename)%(Extension) + + + + + + $(MSBuildThisFileDirectory)..\bin\x64;%(AdditionalLibraryDirectories) + WinPixEventRuntime.lib;%(AdditionalDependencies) + + + + + + $(MSBuildThisFileDirectory)..\bin\ARM64;%(AdditionalLibraryDirectories) + WinPixEventRuntime.lib;%(AdditionalDependencies) + + + + + + %(RecursiveDir)%(FileName)%(Extension) + PreserveNewest + + + + + + %(RecursiveDir)%(FileName)%(Extension) + PreserveNewest + + + diff --git a/Libs/WinPixEventRuntime/package/services/metadata/core-properties/75b52844aad44082a4e6ddea0ac86508.psmdcp b/Libs/WinPixEventRuntime/package/services/metadata/core-properties/75b52844aad44082a4e6ddea0ac86508.psmdcp new file mode 100644 index 00000000..76149abe --- /dev/null +++ b/Libs/WinPixEventRuntime/package/services/metadata/core-properties/75b52844aad44082a4e6ddea0ac86508.psmdcp @@ -0,0 +1 @@ +MicrosoftAllows applications to be instrumented with marker events, for use with Microsoft PIX.WinPixEventRuntime1.0.200127001PIX Direct3D graphics 3DWinPixEventRuntimeNuGet, Version=3.3.0.212, Culture=neutral, PublicKeyToken=31bf3856ad364e35;Microsoft Windows NT 6.2.9200.0;.NET Framework 4.5 \ No newline at end of file diff --git a/Libs/assimp b/Libs/assimp new file mode 160000 index 00000000..9ce06711 --- /dev/null +++ b/Libs/assimp @@ -0,0 +1 @@ +Subproject commit 9ce0671134912fd49010d81b89ae56dc41029c39 diff --git a/README.md b/README.md index e9610f65..8b82186b 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,25 @@ VQE is **VQEngine**: A DX12 rewrite of [VQEngine-Vanilla](https://github.com/vilbeyli/VQEngine) for fast prototyping of rendering techniques and experimenting with cutting-edge technology. + +Join the [VQE Discord Channel](https://discord.gg/U7pd8TV)! + +[![Discord Banner 2](https://discordapp.com/api/guilds/720409073756930079/widget.png?style=banner2)](https://discord.gg/U7pd8TV) + + +# Screenshots + ![](Screenshots/HelloEnvMap.png)

HDRI Environment Map Rendering

+![](Screenshots/HelloModelLoading.png) + +

+Data-driven (XML) Scenes & glTF Model Loading +

+ # Features - Multi-threaded architecture @@ -17,6 +31,7 @@ VQE is **VQEngine**: A DX12 rewrite of [VQEngine-Vanilla](https://github.com/vil - HDR Environment Maps from [HDRI Haven](https://hdrihaven.com/) - HDR display support ![](Screenshots/HDRDisplay.jpg) + - [glTF](https://en.wikipedia.org/wiki/GlTF) [2.0](https://github.com/KhronosGroup/glTF/tree/master/specification/2.0) model loading using [assimp](https://github.com/assimp/assimp) - Multiple windows on multiple monitors - Physically-based Rendering (WIP) - Real-time and offline Ray Tracing (WIP) @@ -52,19 +67,24 @@ Make sure to have installed - [Visual C++ 2019 Redistributiable (x64)](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads) - A DX12-capable GPU -Run `VQE.exe`. +Double click `VQE.exe`. + +Or, if you're using a terminal, +- `VQE.exe -LogConsole` for logging displayed on a console +- `VQE.exe -LogFile="FileName.txt"` to write out to a file. ## Controls | Key | | | :--: | :-- | -| **WASD+EQ** | Camera Movement | +| **WASD+EQ** | Camera movement | | **Page Up/Down** | Change Environment Map | +| **1-4** | Change scenes
**1** - *Environment Map Scene*
**2** - *Sponza*
**3** - *Geometry Test Scene*
**4** - *Stress Test Scene* | +| **Shift+R** | Reload level | +| **C** | Change scene camera | | **V** | Toggle VSync | | **M** | Toggle MSAA | | **Alt+Enter** | Toggle Fullscreen | -| **Spacebar** | Toggle Cube Animation | -| **Mouse Buttons** | Rotate Cube | | **Esc** | Release mouse | @@ -127,4 +147,7 @@ VQE supports the following command line parameters: - [D3DX12](https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Libraries/D3DX12) - [D3D12MA](https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator) - [stb](https://github.com/nothings/stb) -- [tinyxml2](https://github.com/leethomason/tinyxml2) \ No newline at end of file +- [tinyxml2](https://github.com/leethomason/tinyxml2) +- [assimp](https://github.com/assimp/assimp) +- [WinPixEventRuntime](https://devblogs.microsoft.com/pix/winpixeventruntime/) +- [Khronos glTF Sample Models](https://github.com/KhronosGroup/glTF-Sample-Models) \ No newline at end of file diff --git a/Screenshots/HelloModelLoading.png b/Screenshots/HelloModelLoading.png new file mode 100644 index 00000000..c3e559d0 Binary files /dev/null and b/Screenshots/HelloModelLoading.png differ diff --git a/Scripts/TestVQE.bat b/Scripts/TestVQE.bat index c5eeeaca..9090cc33 100644 --- a/Scripts/TestVQE.bat +++ b/Scripts/TestVQE.bat @@ -6,9 +6,9 @@ setlocal enabledelayedexpansion cd ../Build/_artifacts set DIR_BUILD=!CD! -set VQE_REL_DIR=!DIR_BUILD!/Win64 -set VQE_DBG_DIR=!DIR_BUILD!/Win64-Debug -set VQE_RWD_DIR=!DIR_BUILD!/Win64-PDB +set VQE_REL_DIR=!DIR_BUILD! +set VQE_DBG_DIR=!DIR_BUILD! +set VQE_RWD_DIR=!DIR_BUILD! set RUN_TEST_REL=1 set RUN_TEST_DBG=0 @@ -40,9 +40,9 @@ if !errorlevel! NEQ 0 ( exit /b -1 ) -if !RUN_TEST_DBG! NEQ 0 call :RunTest !VQE_DBG_DIR!, VQE.exe +if !RUN_TEST_DBG! NEQ 0 call :RunTest !VQE_DBG_DIR!, VQE-d.exe if !RUN_TEST_REL! NEQ 0 call :RunTest !VQE_REL_DIR!, VQE.exe -if !RUN_TEST_RWD! NEQ 0 call :RunTest !VQE_RWD_DIR!, VQE.exe +if !RUN_TEST_RWD! NEQ 0 call :RunTest !VQE_RWD_DIR!, VQE-rwd.exe exit /b 0 diff --git a/Source/Shaders/FullscreenTriangle.hlsl b/Shaders/FullscreenTriangle.hlsl similarity index 100% rename from Source/Shaders/FullscreenTriangle.hlsl rename to Shaders/FullscreenTriangle.hlsl diff --git a/Shaders/Object.hlsl b/Shaders/Object.hlsl new file mode 100644 index 00000000..51046945 --- /dev/null +++ b/Shaders/Object.hlsl @@ -0,0 +1,93 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +struct VSInput +{ + float4 position : POSITION; + float3 normal : NORMAL; + float3 tangent : TANGENT; + float2 uv : TEXCOORD0; +}; + +struct PSInput +{ + float4 position : SV_POSITION; + float3 vertNormal : COLOR0; + float3 vertTangent : COLOR1; + float2 uv : TEXCOORD0; +}; + +struct PerFrame{}; // TODO +struct PerView {}; // TODO +struct PerObject{}; // TODO + +cbuffer CBuffer : register(b0) +{ + float4x4 matModelViewProj; + int iTextureConfig; + int iTextureOut; +} + + +Texture2D texDiffuse : register(t0); +Texture2D texNormals : register(t1); +Texture2D texEmissive : register(t2); +Texture2D texAlphaMask : register(t3); +Texture2D texMetalness : register(t4); +Texture2D texRoughness : register(t5); + +SamplerState LinearSampler : register(s0); +SamplerState PointSampler : register(s1); + +PSInput VSMain(VSInput vertex) +{ + PSInput result; + + result.position = mul(matModelViewProj, vertex.position); + result.uv = vertex.uv; + result.vertNormal = vertex.normal; + result.vertTangent = vertex.tangent; + + return result; +} + +float4 PSMain(PSInput input) : SV_TARGET +{ + const float2 uv = input.uv; + + float3 Albedo = texDiffuse.SampleLevel(LinearSampler, uv, 0).rgb; + float3 Normal = texNormals.SampleLevel(PointSampler, uv, 0).rgb; + float3 Emissive = texEmissive.SampleLevel(LinearSampler, uv, 0).rgb; + float3 AlphaMask = texAlphaMask.SampleLevel(LinearSampler, uv, 0).rgb; + float3 Metalness = texMetalness.SampleLevel(LinearSampler, uv, 0).rgb; + float3 Roughness = texRoughness.SampleLevel(LinearSampler, uv, 0).rgb; + + float3 OutColor = 0.0.xxx; + switch (iTextureOut) + { + case 0: OutColor = Albedo; break; + case 1: OutColor = Normal; break; + case 2: OutColor = Emissive; break; + case 3: OutColor = AlphaMask; break; + case 4: OutColor = Metalness; break; + case 5: OutColor = Roughness; break; + default: OutColor = float3(0.8, 0, 0.8); break; + } + + return float4(OutColor, 1); +} diff --git a/Source/Shaders/Skydome.hlsl b/Shaders/Skydome.hlsl similarity index 100% rename from Source/Shaders/Skydome.hlsl rename to Shaders/Skydome.hlsl diff --git a/Source/Shaders/Tonemapper.hlsl b/Shaders/Tonemapper.hlsl similarity index 100% rename from Source/Shaders/Tonemapper.hlsl rename to Shaders/Tonemapper.hlsl diff --git a/Source/Shaders/hello-cube.hlsl b/Shaders/hello-cube.hlsl similarity index 100% rename from Source/Shaders/hello-cube.hlsl rename to Shaders/hello-cube.hlsl diff --git a/Source/Shaders/hello-triangle.hlsl b/Shaders/hello-triangle.hlsl similarity index 100% rename from Source/Shaders/hello-triangle.hlsl rename to Shaders/hello-triangle.hlsl diff --git a/Source/Application/AssetLoader.cpp b/Source/Application/AssetLoader.cpp new file mode 100644 index 00000000..4ed45b6a --- /dev/null +++ b/Source/Application/AssetLoader.cpp @@ -0,0 +1,620 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#include "AssetLoader.h" +#include "Mesh.h" +#include "Material.h" +#include "Scene.h" + +#include "../Renderer/Renderer.h" + +#include "Libs/VQUtils/Source/Multithreading.h" +#include "Libs/VQUtils/Source/utils.h" +#include "Libs/VQUtils/Source/Image.h" +#include "Libs/VQUtils/Source/Timer.h" +#include "Libs/VQUtils/Source/Log.h" + +#include +#include +#include + +using namespace Assimp; +using namespace DirectX; + +AssetLoader::LoadTaskID AssetLoader::GenerateLoadTaskID() +{ + static std::atomic LOAD_TASK_ID = 0; + LoadTaskID id = LOAD_TASK_ID.fetch_add(1); + return id; +} + +AssetLoader::AssetLoader(ThreadPool& WorkerThreads_Model, ThreadPool& WorkerThreads_Texture, VQRenderer& renderer) + : mWorkers_ModelLoad(WorkerThreads_Model) + , mWorkers_TextureLoad(WorkerThreads_Texture) + , mRenderer(renderer) +{} + +//---------------------------------------------------------------------------------------------------------------- +// ASSET LOADER +//---------------------------------------------------------------------------------------------------------------- +void AssetLoader::QueueModelLoad(GameObject* pObject, const std::string& ModelPath, const std::string& ModelName) +{ + const std::string FileExtension = DirectoryUtil::GetFileExtension(ModelPath); + + std::unique_lock lk(mMtxQueue_ModelLoad); + mModelLoadQueue.push({pObject, ModelPath, ModelName, AssetLoader::ImportModel }); +} + +AssetLoader::ModelLoadResults_t AssetLoader::StartLoadingModels(Scene* pScene) +{ + VQRenderer* pRenderer = &mRenderer; + ModelLoadResults_t ModelLoadResults; + + if (mModelLoadQueue.empty()) + { + Log::Warning("AssetLoader::StartLoadingModels(): no models to load"); + return ModelLoadResults; + } + + // process model load queue + std::unordered_map> ModelLoadResultMap; + std::unique_lock lk(mMtxQueue_ModelLoad); + do + { + FModelLoadParams ModelLoadParams = std::move(mModelLoadQueue.front()); + const std::string& ModelPath = ModelLoadParams.ModelPath; + mModelLoadQueue.pop(); + + // queue unique model paths for loading + std::shared_future modelLoadResult; + if (mUniqueModelPaths.find(ModelPath) == mUniqueModelPaths.end()) + { + mUniqueModelPaths.insert(ModelPath); + + // check whether Exit signal is given to the app before dispatching workers + if (mWorkers_ModelLoad.IsExiting() || mWorkers_TextureLoad.IsExiting()) + { + break; + } + + // start loading the model + modelLoadResult = std::move(mWorkers_ModelLoad.AddTask([=]() + { + return ModelLoadParams.pfnImportModel(pScene, this, pRenderer, ModelLoadParams.ModelPath, ModelLoadParams.ModelName); + })); + ModelLoadResultMap[ModelLoadParams.ModelPath] = modelLoadResult; + } + else + { + modelLoadResult = ModelLoadResultMap.at(ModelLoadParams.ModelPath); + } + + ModelLoadResults.emplace(std::make_pair(ModelLoadParams.pObject, modelLoadResult)); + + } while (!mModelLoadQueue.empty()); + + mUniqueModelPaths.clear(); + return ModelLoadResults; +} + +void AssetLoader::QueueTextureLoad(LoadTaskID taskID, const FTextureLoadParams& TexLoadParam) +{ + FLoadTaskContext& ctx = mLookup_TextureLoadContext[taskID]; + std::unique_lock lk(ctx.Mtx); + ctx.LoadQueue.push(TexLoadParam); +} + +AssetLoader::TextureLoadResults_t AssetLoader::StartLoadingTextures(LoadTaskID taskID) +{ + TextureLoadResults_t TextureLoadResults; + FLoadTaskContext& ctx = mLookup_TextureLoadContext.at(taskID); + + if (ctx.LoadQueue.empty()) + { + Log::Warning("AssetLoader::StartLoadingTextures(taskID=%d): no Textures to load", taskID); + return TextureLoadResults; + } + + std::unordered_map> Lookup_TextureLoadResult; + + // process model load queue + do + { + FTextureLoadParams TexLoadParams = std::move(ctx.LoadQueue.front()); + ctx.LoadQueue.pop(); + + // handle duplicate texture load paths + if (ctx.UniquePaths.find(TexLoadParams.TexturePath) == ctx.UniquePaths.end()) + { + ctx.UniquePaths.insert(TexLoadParams.TexturePath); + + // determine whether we'll load file OR use a procedurally generated texture + auto vPathTokens = StrUtil::split(TexLoadParams.TexturePath, '/'); + assert(!vPathTokens.empty()); + const bool bProceduralTexture = vPathTokens[0] == "Procedural"; + + EProceduralTextures ProcTex = bProceduralTexture + ? VQRenderer::GetProceduralTextureEnumFromName(vPathTokens[1]) + : EProceduralTextures::NUM_PROCEDURAL_TEXTURES; + + // check whether Exit signal is given to the app before dispatching workers + if (mWorkers_TextureLoad.IsExiting()) + { + break; + } + + // dispatch worker thread + std::shared_future texLoadResult = std::move(mWorkers_TextureLoad.AddTask([this, TexLoadParams, ProcTex]() + { + const bool IS_PROCEDURAL = ProcTex != EProceduralTextures::NUM_PROCEDURAL_TEXTURES; + if (IS_PROCEDURAL) + { + return mRenderer.GetProceduralTexture(ProcTex); + } + + return mRenderer.CreateTextureFromFile(TexLoadParams.TexturePath.c_str()); + })); + + // update results lookup for the shared textures (among different materials) + Lookup_TextureLoadResult[TexLoadParams.TexturePath] = texLoadResult; + + // record load results + TextureLoadResults.emplace(std::make_pair(TexLoadParams.MatID, TextureLoadResult_t{ TexLoadParams.TexType, std::move(texLoadResult) })); + } + // shared textures among materials + else + { + TextureLoadResults.emplace(std::make_pair(TexLoadParams.MatID, TextureLoadResult_t{ TexLoadParams.TexType, Lookup_TextureLoadResult.at(TexLoadParams.TexturePath) })); + } + } while (!ctx.LoadQueue.empty()); + + ctx.UniquePaths.clear(); + + // Currently mRenderer.CreateTextureFromFile() starts the texture uploads + ///mRenderer.StartTextureUploads(); + + mLookup_TextureLoadContext.erase(taskID); + + return std::move(TextureLoadResults); +} + + + +static AssetLoader::ETextureType GetTextureType(aiTextureType aiType) +{ + switch (aiType) + { + case aiTextureType_NONE: return AssetLoader::ETextureType::NUM_TEXTURE_TYPES; break; + case aiTextureType_DIFFUSE: return AssetLoader::ETextureType::DIFFUSE; break; + case aiTextureType_SPECULAR: return AssetLoader::ETextureType::SPECULAR; break; + case aiTextureType_EMISSIVE: return AssetLoader::ETextureType::EMISSIVE; break; + case aiTextureType_HEIGHT: return AssetLoader::ETextureType::HEIGHT; break; + case aiTextureType_NORMALS: return AssetLoader::ETextureType::NORMALS; break; + case aiTextureType_OPACITY: return AssetLoader::ETextureType::ALPHA_MASK; break; + case aiTextureType_METALNESS: return AssetLoader::ETextureType::METALNESS; break; + case aiTextureType_AMBIENT: break; + case aiTextureType_SHININESS: break; + case aiTextureType_DISPLACEMENT: + break; + case aiTextureType_LIGHTMAP: + break; + case aiTextureType_REFLECTION: + break; + case aiTextureType_BASE_COLOR: + break; + case aiTextureType_NORMAL_CAMERA: + break; + case aiTextureType_EMISSION_COLOR: + break; + case aiTextureType_DIFFUSE_ROUGHNESS: + break; + case aiTextureType_AMBIENT_OCCLUSION: + break; + case aiTextureType_UNKNOWN: + break; + case _aiTextureType_Force32Bit: + break; + default: + break; + } + return AssetLoader::ETextureType::NUM_TEXTURE_TYPES; +} + +void AssetLoader::FMaterialTextureAssignments::DoAssignments(Scene* pScene, VQRenderer* pRenderer) +{ + for (FMaterialTextureAssignment& assignment : mAssignments) + { + Material& mat = pScene->GetMaterial(assignment.matID); + + bool bFound = mTextureLoadResults.find(assignment.matID) != mTextureLoadResults.end(); + if (!bFound) + { + Log::Error("TextureLoadResutls for MatID=%d not found!", assignment.matID); + continue; + } + + auto pair_itBeginEnd = mTextureLoadResults.equal_range(assignment.matID); + for (auto it = pair_itBeginEnd.first; it != pair_itBeginEnd.second; ++it) + { + const MaterialID& matID = it->first; + TextureLoadResult_t& result = it->second; + + if (mWorkersThreads.IsExiting()) + break; + + assert(result.texLoadResult.valid()); + + result.texLoadResult.wait(); + switch (result.type) + { + case DIFFUSE : mat.TexDiffuseMap = result.texLoadResult.get(); break; + case NORMALS : mat.TexNormalMap = result.texLoadResult.get(); break; + case ALPHA_MASK : mat.TexAlphaMaskMap = result.texLoadResult.get(); break; + case EMISSIVE : mat.TexEmissiveMap = result.texLoadResult.get(); break; + case METALNESS : mat.TexMetallicMap = result.texLoadResult.get(); break; + case ROUGHNESS : mat.TexRoughnessMap = result.texLoadResult.get(); break; + case SPECULAR : mat.TexSpecularMap = result.texLoadResult.get(); /*pRenderer->InitializeSRV(mat.SRVMaterialMaps, 0, mat.TexSpecularMap);*/ break; + case HEIGHT : mat.TexHeightMap = result.texLoadResult.get(); /*pRenderer->InitializeSRV(mat.SRVMaterialMaps, 0, mat.TexHeightMap) ;*/ break; + default: + Log::Warning("TODO"); + break; + } + } + + pRenderer->InitializeSRV(mat.SRVMaterialMaps, EMaterialTextureMapBindings::ALBEDO, mat.TexDiffuseMap); + pRenderer->InitializeSRV(mat.SRVMaterialMaps, EMaterialTextureMapBindings::NORMALS, mat.TexNormalMap); + pRenderer->InitializeSRV(mat.SRVMaterialMaps, EMaterialTextureMapBindings::ALPHA_MASK, mat.TexAlphaMaskMap); + pRenderer->InitializeSRV(mat.SRVMaterialMaps, EMaterialTextureMapBindings::EMISSIVE, mat.TexEmissiveMap); + pRenderer->InitializeSRV(mat.SRVMaterialMaps, EMaterialTextureMapBindings::METALLIC, mat.TexMetallicMap); + pRenderer->InitializeSRV(mat.SRVMaterialMaps, EMaterialTextureMapBindings::ROUGHNESS, mat.TexRoughnessMap); + } +} + +void AssetLoader::FMaterialTextureAssignments::WaitForTextureLoads() +{ + for (auto it = mTextureLoadResults.begin(); it != mTextureLoadResults.end(); ++it) + { + const MaterialID& matID = it->first; + const TextureLoadResult_t& result = it->second; + assert(result.texLoadResult.valid()); + + if (mWorkersThreads.IsExiting()) + break; + + result.texLoadResult.wait(); + } +} + +std::vector& AssetLoader::FMaterialTextureAssignment::GetTextureMapCollection(ETextureType type) +{ + switch (type) + { + case AssetLoader::DIFFUSE : return DiffuseIDs; + case AssetLoader::NORMALS : return NormalsIDs; + case AssetLoader::SPECULAR : assert(false); return DiffuseIDs; // currently not supported + case AssetLoader::ALPHA_MASK : return AlphaMapIDs; + case AssetLoader::EMISSIVE : assert(false); return DiffuseIDs; // TODO + case AssetLoader::HEIGHT : return HeightMapIDs; + case AssetLoader::METALNESS : assert(false); return DiffuseIDs; // TODO + case AssetLoader::ROUGHNESS : assert(false); return DiffuseIDs; // TODO + } + assert(false); return DiffuseIDs; +} + + +//---------------------------------------------------------------------------------------------------------------- +// ASSIMP HELPER FUNCTIONS +//---------------------------------------------------------------------------------------------------------------- +static std::vector GenerateTextureLoadParams( + aiMaterial* pMaterial + , MaterialID matID + , aiTextureType type + , const std::string& textureName + , const std::string& modelDirectory +) +{ + std::vector TexLoadParams; + for (unsigned int i = 0; i < pMaterial->GetTextureCount(type); ++i) + { + aiString str; + pMaterial->GetTexture(type, i, &str); + std::string path = str.C_Str(); + AssetLoader::FTextureLoadParams params = {}; + params.TexturePath = modelDirectory + path; + params.MatID = matID; + params.TexType = GetTextureType(type); + TexLoadParams.push_back(params); + } + return TexLoadParams; +} + +static Mesh ProcessAssimpMesh( + VQRenderer* pRenderer + , aiMesh* pMesh + , const aiScene* pScene + , const std::string& ModelName +) +{ + std::vector Vertices; + std::vector Indices; + + // Walk through each of the mesh's vertices + for (unsigned int i = 0; i < pMesh->mNumVertices; i++) + { + FVertexWithNormalAndTangent Vert; + + // POSITIONS + Vert.position[0] = pMesh->mVertices[i].x; + Vert.position[1] = pMesh->mVertices[i].y; + Vert.position[2] = pMesh->mVertices[i].z; + + // TEXTURE COORDINATES + // a vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't + // use models where a vertex can have multiple texture coordinates so we always take the first set (0). + Vert.uv[0] = pMesh->mTextureCoords[0] ? pMesh->mTextureCoords[0][i].x : 0; + Vert.uv[1] = pMesh->mTextureCoords[0] ? pMesh->mTextureCoords[0][i].y : 0; + + // NORMALS + if (pMesh->mNormals) + { + Vert.normal[0] = pMesh->mNormals[i].x; + Vert.normal[1] = pMesh->mNormals[i].y; + Vert.normal[2] = pMesh->mNormals[i].z; + } + + // TANGENT + if (pMesh->mTangents) + { + Vert.tangent[0] = pMesh->mTangents[i].x; + Vert.tangent[1] = pMesh->mTangents[i].y; + Vert.tangent[2] = pMesh->mTangents[i].z; + } + + // BITANGENT ( NOT USED ) + // Vert.bitangent = XMFLOAT3( + // mesh->mBitangents[i].x, + // mesh->mBitangents[i].y, + // mesh->mBitangents[i].z + // ); + Vertices.push_back(Vert); + } + + // now walk through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices. + for (unsigned int i = 0; i < pMesh->mNumFaces; i++) + { + aiFace face = pMesh->mFaces[i]; + // retrieve all indices of the face and store them in the indices vector + for (unsigned int j = 0; j < face.mNumIndices; j++) + Indices.push_back(face.mIndices[j]); + } + + return Mesh(pRenderer, Vertices, Indices, ModelName);; +} + +static Model::Data ProcessAssimpNode( + const std::string& ModelName, + aiNode* const pNode, + const aiScene* pAiScene, + const std::string& modelDirectory, + AssetLoader* pAssetLoader, + Scene* pScene, + VQRenderer* pRenderer, + AssetLoader::FMaterialTextureAssignments& MaterialTextureAssignments, + AssetLoader::LoadTaskID taskID +) +{ + Model::Data modelData; + + for (unsigned int i = 0; i < pNode->mNumMeshes; i++) + { // process all the node's meshes (if any) + aiMesh* pAiMesh = pAiScene->mMeshes[pNode->mMeshes[i]]; + + // MATERIAL - http://assimp.sourceforge.net/lib_html/materials.html + aiMaterial* material = pAiScene->mMaterials[pAiMesh->mMaterialIndex]; + + // Every material assumed to have a name + std::string uniqueMatName; + { + aiString matName; + if (aiReturn_SUCCESS != material->Get(AI_MATKEY_NAME, matName)) + // material doesn't have a name, use generic name Material# + { + matName = std::string("Material#") + std::to_string(pAiMesh->mMaterialIndex); + } + + // Data/Models/%MODEL_NAME%/... : index 2 will give model name + auto vFolders = DirectoryUtil::GetFlattenedFolderHierarchy(modelDirectory); + assert(vFolders.size() > 2); + const std::string ModelFolderName = vFolders[2]; + uniqueMatName = matName.C_Str(); + // modelDirectory = "Data/Models/%MODEL_NAME%/" + // Materials use the following unique naming: %MODEL_NAME%/%MATERIAL_NAME% + uniqueMatName = ModelFolderName + "/" + uniqueMatName; + } + + // Create new Material + MaterialID matID = pScene->CreateMaterial(uniqueMatName); + Material& mat = pScene->GetMaterial(matID); + + // get texture paths to load + AssetLoader::FMaterialTextureAssignment MatTexAssignment = {}; + std::vector diffuseMaps = GenerateTextureLoadParams(material, matID, aiTextureType_DIFFUSE, "texture_diffuse", modelDirectory); + std::vector specularMaps = GenerateTextureLoadParams(material, matID, aiTextureType_SPECULAR, "texture_specular", modelDirectory); + std::vector normalMaps = GenerateTextureLoadParams(material, matID, aiTextureType_NORMALS, "texture_normal", modelDirectory); + std::vector heightMaps = GenerateTextureLoadParams(material, matID, aiTextureType_HEIGHT, "texture_height", modelDirectory); + std::vector alphaMaps = GenerateTextureLoadParams(material, matID, aiTextureType_OPACITY, "texture_alpha", modelDirectory); + + // queue texture load + std::array TexLoadParams = { &diffuseMaps, &specularMaps, &normalMaps, &heightMaps, &alphaMaps }; + int iTexType = 0; + for (const auto* pvLoadParams : TexLoadParams) + { + int iTex = 0; + for (const AssetLoader::FTextureLoadParams& param : *pvLoadParams) + { + pAssetLoader->QueueTextureLoad(taskID, param); + ++iTex; + } + ++iTexType; + } + + + // unflatten the material texture assignments + MatTexAssignment.matID = matID; + MaterialTextureAssignments.mAssignments.push_back(std::move(MatTexAssignment)); + + aiColor3D color(0.f, 0.f, 0.f); + if (aiReturn_SUCCESS == material->Get(AI_MATKEY_COLOR_DIFFUSE, color)) + { + mat.diffuse = XMFLOAT3(color.r, color.g, color.b); + } + + aiColor3D specular(0.f, 0.f, 0.f); + if (aiReturn_SUCCESS == material->Get(AI_MATKEY_COLOR_SPECULAR, specular)) + { + mat.specular = XMFLOAT3(specular.r, specular.g, specular.b); + } + + aiColor3D transparent(0.0f, 0.0f, 0.0f); + if (aiReturn_SUCCESS == material->Get(AI_MATKEY_COLOR_TRANSPARENT, transparent)) + { // Defines the transparent color of the material, this is the color to be multiplied + // with the color of translucent light to construct the final 'destination color' + // for a particular position in the screen buffer. T + // + int a = 5; + } + + float opacity = 0.0f; + if (aiReturn_SUCCESS == material->Get(AI_MATKEY_OPACITY, opacity)) + { + mat.alpha = opacity; + } + + float shininess = 0.0f; + if (aiReturn_SUCCESS == material->Get(AI_MATKEY_SHININESS, shininess)) + { + // Phong Shininess -> Beckmann BRDF Roughness conversion + // + // https://simonstechblog.blogspot.com/2011/12/microfacet-brdf.html + // https://computergraphics.stackexchange.com/questions/1515/what-is-the-accepted-method-of-converting-shininess-to-roughness-and-vice-versa + // + mat.roughness = sqrtf(2.0f / (2.0f + shininess)); + } + + // other material keys to consider + // + // AI_MATKEY_TWOSIDED + // AI_MATKEY_ENABLE_WIREFRAME + // AI_MATKEY_BLEND_FUNC + // AI_MATKEY_BUMPSCALING + + Mesh mesh = ProcessAssimpMesh(pRenderer, pAiMesh, pAiScene, ModelName); + MeshID id = pScene->AddMesh(std::move(mesh)); + modelData.mOpaueMeshIDs.push_back(id); + + modelData.mOpaqueMaterials[modelData.mOpaueMeshIDs.back()] = matID; + if (mat.IsTransparent()) + { + modelData.mTransparentMeshIDs.push_back(modelData.mOpaueMeshIDs.back()); + } + } // for: NumMeshes + + for (unsigned int i = 0; i < pNode->mNumChildren; i++) + { // then do the same for each of its children + Model::Data childModelData = ProcessAssimpNode(ModelName, pNode->mChildren[i], pAiScene, modelDirectory, pAssetLoader, pScene, pRenderer, MaterialTextureAssignments, taskID); + std::vector& ChildMeshes = childModelData.mOpaueMeshIDs; + std::vector& ChildMeshesTransparent = childModelData.mTransparentMeshIDs; + + std::copy(ChildMeshes.begin(), ChildMeshes.end(), std::back_inserter(modelData.mOpaueMeshIDs)); + std::copy(ChildMeshesTransparent.begin(), ChildMeshesTransparent.end(), std::back_inserter(modelData.mTransparentMeshIDs)); + for (auto& kvp : childModelData.mOpaqueMaterials) + { +#if _DEBUG + assert(modelData.mOpaqueMaterials.find(kvp.first) == modelData.mOpaqueMaterials.end()); +#endif + modelData.mOpaqueMaterials[kvp.first] = kvp.second; + } + } // for: NumChildren + + return modelData; +} + + +//---------------------------------------------------------------------------------------------------------------- +// IMPORT MODEL FUNCTION FOR WORKER THREADS +//---------------------------------------------------------------------------------------------------------------- +ModelID AssetLoader::ImportModel(Scene* pScene, AssetLoader* pAssetLoader, VQRenderer* pRenderer, const std::string& objFilePath, std::string ModelName) +{ + constexpr auto ASSIMP_LOAD_FLAGS + = aiProcess_Triangulate + | aiProcess_CalcTangentSpace + | aiProcess_MakeLeftHanded + | aiProcess_FlipUVs + | aiProcess_FlipWindingOrder + //| aiProcess_TransformUVCoords + //| aiProcess_FixInfacingNormals + | aiProcess_JoinIdenticalVertices + | aiProcess_GenSmoothNormals; + + + LoadTaskID taskID = GenerateLoadTaskID(); + //----------------------------------------------- + const std::string modelDirectory = DirectoryUtil::GetFolderPath(objFilePath); + + Log::Info("ImportModel: %s - %s", ModelName.c_str(), objFilePath.c_str()); + Timer t; + t.Start(); + + // Import Assimp Scene + Importer importer; + const aiScene* pAiScene = importer.ReadFile(objFilePath, ASSIMP_LOAD_FLAGS); + if (!pAiScene || pAiScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !pAiScene->mRootNode) + { + Log::Error("Assimp error: %s", importer.GetErrorString()); + return INVALID_ID; + } + t.Tick(); float fTimeReadFile = t.DeltaTime(); + Log::Info(" [%.2fs] ReadFile=%s ", fTimeReadFile, objFilePath.c_str()); + + // parse scene and initialize model data + FMaterialTextureAssignments MaterialTextureAssignments(pAssetLoader->mWorkers_TextureLoad); + Model::Data data = ProcessAssimpNode(ModelName, pAiScene->mRootNode, pAiScene, modelDirectory, pAssetLoader, pScene, pRenderer, MaterialTextureAssignments, taskID); + + pRenderer->UploadVertexAndIndexBufferHeaps(); // load VB/IBs + + if (!MaterialTextureAssignments.mAssignments.empty()) + MaterialTextureAssignments.mTextureLoadResults = pAssetLoader->StartLoadingTextures(taskID); + + // cache the imported model in Scene + ModelID mID = pScene->CreateModel(); + Model& model = pScene->GetModel(mID); + model = Model(objFilePath, ModelName, std::move(data)); + + // SYNC POINT : wait for textures to load + { + MaterialTextureAssignments.WaitForTextureLoads(); + } + + // assign TextureIDs to the materials; + MaterialTextureAssignments.DoAssignments(pScene, pRenderer); + + t.Stop(); + Log::Info(" [%.2fs] Loaded Model '%s'.", fTimeReadFile + t.DeltaTime(), ModelName.c_str()); + return mID; +} + diff --git a/Source/Application/AssetLoader.h b/Source/Application/AssetLoader.h new file mode 100644 index 00000000..f8f53e2b --- /dev/null +++ b/Source/Application/AssetLoader.h @@ -0,0 +1,126 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com +#pragma once + +#include "Model.h" + +#include +#include +#include +#include + +class ThreadPool; +class Scene; +class GameObject; + +class AssetLoader +{ +public: + enum ETextureType + { + DIFFUSE = 0, + NORMALS, + SPECULAR, + ALPHA_MASK, + EMISSIVE, + HEIGHT, + METALNESS, + ROUGHNESS, + + NUM_TEXTURE_TYPES + }; + struct FModelLoadParams + { + using pfnImportModel_t = ModelID(*)(Scene* pScene, AssetLoader* pAssetLoader, VQRenderer* pRenderer, const std::string& objFilePath, std::string ModelName); + + GameObject* pObject = nullptr; + std::string ModelPath; + std::string ModelName; + pfnImportModel_t pfnImportModel = nullptr; + }; + struct FTextureLoadParams + { + ETextureType TexType; + MaterialID MatID; + std::string TexturePath; + }; + struct FTextureLoadResult + { + ETextureType type; // material textures: diffuse/normal/alpha_mask/... + std::shared_future texLoadResult; + }; + using ModelLoadResult_t = std::shared_future; + using ModelLoadResults_t = std::unordered_map; + using TextureLoadResult_t = FTextureLoadResult; + using TextureLoadResults_t = std::unordered_multimap; + struct FMaterialTextureAssignment + { + MaterialID matID = INVALID_ID; + std::vector DiffuseIDs; + std::vector SpecularIDs; + std::vector NormalsIDs; + std::vector HeightMapIDs; + std::vector AlphaMapIDs; + std::vector& GetTextureMapCollection(ETextureType type); + }; + struct FMaterialTextureAssignments + { + FMaterialTextureAssignments(const ThreadPool& workers) : mWorkersThreads(workers) {} + void DoAssignments(Scene* pScene, VQRenderer* pRenderer); + void WaitForTextureLoads(); + + const ThreadPool& mWorkersThreads; // to check if pool IsExiting() + std::vector mAssignments; + TextureLoadResults_t mTextureLoadResults; + }; +public: + using LoadTaskID = int; + static LoadTaskID GenerateLoadTaskID(); + + AssetLoader(ThreadPool& WorkerThreads_Model, ThreadPool& WorkerThreads_Texture, VQRenderer& renderer); + inline const ThreadPool& GetThreadPool_TextureLoad() const { return mWorkers_TextureLoad; } + + void QueueModelLoad(GameObject* pObject, const std::string& ModelPath, const std::string& ModelName); + ModelLoadResults_t StartLoadingModels(Scene* pScene); + + void QueueTextureLoad(LoadTaskID taskID, const FTextureLoadParams& TexLoadParam); + TextureLoadResults_t StartLoadingTextures(LoadTaskID taskID); + +private: + static ModelID ImportModel(Scene* pScene, AssetLoader* pAssetLoader, VQRenderer* pRenderer, const std::string& objFilePath, std::string ModelName = "NONE"); + +private: + ThreadPool& mWorkers_ModelLoad; + ThreadPool& mWorkers_TextureLoad; + VQRenderer& mRenderer; + + template struct FLoadTaskContext + { + std::mutex Mtx; + std::queue LoadQueue; + std::set UniquePaths; + }; + std::unordered_map> mLookup_TextureLoadContext; + + // TODO: use ConcurrentQueue with ProcessElements(pfnProcess); + std::queue mModelLoadQueue; + std::set mUniqueModelPaths; + std::mutex mMtxQueue_ModelLoad; + + std::unordered_map mLoadedModels; +}; \ No newline at end of file diff --git a/Source/Application/Camera.cpp b/Source/Application/Camera.cpp index fd966eaf..8b1cbe6b 100644 --- a/Source/Application/Camera.cpp +++ b/Source/Application/Camera.cpp @@ -1,5 +1,5 @@ -// VQEngine | DirectX11 Renderer -// Copyright(C) 2018 - Volkan Ilbeyli +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli // // This program is free software : you can redistribute it and / or modify // it under the terms of the GNU General Public License as published by @@ -17,20 +17,17 @@ // Contact: volkanilbeyli@gmail.com #include "Camera.h" +#include "Input.h" #define CAMERA_DEBUG 1 using namespace DirectX; Camera::Camera() - : - MoveSpeed(1000.0f), - AngularSpeedDeg(0.05f), - Drag(9.5f), - mPitch(0.0f), - mYaw(0.0f), - mPosition(0,0,0), - mVelocity(0,0,0) + : mPitch(0.0f) + , mYaw(0.0f) + , mPosition(0,0,0) + , mVelocity(0,0,0) { XMStoreFloat4x4(&mMatProj, XMMatrixIdentity()); XMStoreFloat4x4(&mMatView, XMMatrixIdentity()); @@ -39,38 +36,65 @@ Camera::Camera() Camera::~Camera(void) {} -void Camera::InitializeCamera(const FCameraData& data) +Camera::Camera(Camera && other) { - const auto& NEAR_PLANE = data.nearPlane; - const auto& FAR_PLANE = data.farPlane; - const float AspectRatio = data.width / data.height; - const float VerticalFoV = data.fovV_Degrees * DEG2RAD; - const float& ViewportX = data.width; - const float& ViewportY = data.height; + mPosition = other.mPosition; + mYaw = other.mYaw; + mVelocity = other.mVelocity; + mPitch = other.mPitch; + mProjParams = other.mProjParams; + mMatProj = other.mMatProj; + mMatView = other.mMatView; + pController = std::move(other.pController); +} - this->mProjParams.NearZ = NEAR_PLANE; - this->mProjParams.FarZ = FAR_PLANE; - this->mProjParams.ViewporHeight = ViewportY; - this->mProjParams.ViewporWidth = ViewportX; - this->mProjParams.FieldOfView = data.fovV_Degrees * DEG2RAD; - this->mProjParams.bPerspectiveProjection = data.bPerspectiveProjection; +Camera Camera::Clone() +{ + Camera c = {}; + c.mPosition = this->mPosition; + c.mYaw = this->mYaw; + c.mVelocity = this->mVelocity; + c.mPitch = this->mPitch; + c.mProjParams = this->mProjParams; + c.mMatProj = this->mMatProj; + c.mMatView = this->mMatView; + c.pController = std::move(this->pController->Clone(&c)); + return c; // is this dangling too? +} + +void Camera::InitializeCamera(const FCameraParameters& data) +{ + this->mProjParams = data.ProjectionParams; + this->mProjParams.FieldOfView *= DEG2RAD; // convert FoV to radians + this->mYaw = this->mPitch = 0; // set with Rotate() below + - mYaw = mPitch = 0; SetProjectionMatrix(this->mProjParams); SetPosition(data.x, data.y, data.z); - Rotate(data.yaw * DEG2RAD, data.pitch * DEG2RAD, 1.0f); + Rotate(data.Yaw * DEG2RAD, data.Pitch * DEG2RAD); UpdateViewMatrix(); } +void Camera::InitializeController(bool bFirstPersonController, const FCameraParameters& data) +{ + if (bFirstPersonController) + { + pController = std::make_unique(this, data.TranslationSpeed, data.AngularSpeed, data.Drag); + } + else + { + pController = std::make_unique(this); + } +} -void Camera::SetProjectionMatrix(const ProjectionMatrixParameters& params) +void Camera::SetProjectionMatrix(const FProjectionMatrixParameters& params) { - assert(params.ViewporHeight > 0.0f); - const float AspectRatio = params.ViewporWidth / params.ViewporHeight; + assert(params.ViewportHeight > 0.0f); + const float AspectRatio = params.ViewportWidth / params.ViewportHeight; mMatProj = params.bPerspectiveProjection ? MakePerspectiveProjectionMatrix(params.FieldOfView, AspectRatio, params.NearZ, params.FarZ) - : MakeOthographicProjectionMatrix(params.ViewporWidth, params.ViewporHeight, params.NearZ, params.FarZ); + : MakeOthographicProjectionMatrix(params.ViewportWidth, params.ViewportHeight, params.NearZ, params.FarZ); } void Camera::UpdateViewMatrix() @@ -90,21 +114,6 @@ void Camera::UpdateViewMatrix() XMStoreFloat4x4(&mMatView, XMMatrixLookAtLH(pos, lookAt, up)); } -void Camera::Update(const float dt, const FCameraInput& input) -{ - Rotate(dt, input); - Move(dt, input); - - UpdateViewMatrix(); - - // move based on velocity - XMVECTOR P = XMLoadFloat3(&mPosition); - XMVECTOR V = XMLoadFloat3(&mVelocity); - P += V * dt; - XMStoreFloat3(&mPosition, P); -} - - XMFLOAT3 Camera::GetPositionF() const { return mPosition; @@ -167,31 +176,94 @@ void Camera::SetPosition(float x, float y, float z) mPosition = XMFLOAT3(x, y, z); } -void Camera::Rotate(float yaw, float pitch, const float dt) +void Camera::Rotate(float yaw, float pitch) { - mYaw += yaw * dt; - mPitch += pitch * dt; + mYaw += yaw; + mPitch += pitch; if (mPitch > +90.0f * DEG2RAD) mPitch = +90.0f * DEG2RAD; if (mPitch < -90.0f * DEG2RAD) mPitch = -90.0f * DEG2RAD; } -// internal update functions -void Camera::Rotate(const float dt, const FCameraInput& input) +//============================================================================================================== + + +OrbitController::OrbitController(Camera* pCam) + : CameraController(pCam) +{ +} + +void OrbitController::UpdateCamera(const Input& input, float dt) + { - const float& dy = input.DeltaMouseXY[1]; - const float& dx = input.DeltaMouseXY[0]; +} - const float delta = AngularSpeedDeg * DEG2RAD; // rotation doesn't depend on time - Rotate(dx, dy, delta); +CameraController* OrbitController::Clone_impl(Camera* pNewCam) +{ + OrbitController* p = new OrbitController(pNewCam); + p->mF3LookAt = this->mF3LookAt; + return p; } -void Camera::Move(const float dt, const FCameraInput& input) +FirstPersonController::FirstPersonController(Camera* pCam + , float moveSpeed /*= 1000.0f*/ + , float angularSpeed /*= 0.05f */ + , float drag /*= 9.5f */ +) + : CameraController(pCam) + , MoveSpeed(moveSpeed) + , AngularSpeedDeg(angularSpeed) + , Drag(drag) +{} + +void FirstPersonController::UpdateCamera(const Input& input, float dt) { - const XMMATRIX MRotation = GetRotationMatrix(); - const XMVECTOR WorldSpaceTranslation = XMVector3TransformCoord(input.LocalTranslationVector, MRotation); + constexpr float CAMERA_MOVEMENT_SPEED_MULTIPLER = 0.75f; + constexpr float CAMERA_MOVEMENT_SPEED_SHIFT_MULTIPLER = 2.0f; + + XMVECTOR LocalSpaceTranslation = XMVectorSet(0, 0, 0, 0); + if (input.IsKeyDown('A')) LocalSpaceTranslation += XMLoadFloat3(&LeftVector); + if (input.IsKeyDown('D')) LocalSpaceTranslation += XMLoadFloat3(&RightVector); + if (input.IsKeyDown('W')) LocalSpaceTranslation += XMLoadFloat3(&ForwardVector); + if (input.IsKeyDown('S')) LocalSpaceTranslation += XMLoadFloat3(&BackVector); + if (input.IsKeyDown('E')) LocalSpaceTranslation += XMLoadFloat3(&UpVector); + if (input.IsKeyDown('Q')) LocalSpaceTranslation += XMLoadFloat3(&DownVector); + if (input.IsKeyDown(VK_SHIFT)) LocalSpaceTranslation *= CAMERA_MOVEMENT_SPEED_SHIFT_MULTIPLER; + LocalSpaceTranslation *= CAMERA_MOVEMENT_SPEED_MULTIPLER; + + // update camera + FCameraInput camInput(LocalSpaceTranslation); + camInput.DeltaMouseXY = input.GetMouseDelta(); + - XMVECTOR V = XMLoadFloat3(&mVelocity); + //this->mpCamera->Update(dt, camInput); + const float RotationSpeed = this->AngularSpeedDeg * DEG2RAD; // rotation doesn't depend on time + const float dy = camInput.DeltaMouseXY[1] * RotationSpeed; + const float dx = camInput.DeltaMouseXY[0] * RotationSpeed; + this->mpCamera->Rotate(dx, dy); + + + //this->mpCamera->Move(dt, camInput); + const XMMATRIX MRotation = this->mpCamera->GetRotationMatrix(); + const XMVECTOR WorldSpaceTranslation = XMVector3TransformCoord(camInput.LocalTranslationVector, MRotation); + + XMVECTOR V = XMLoadFloat3(&this->mpCamera->mVelocity); V += (WorldSpaceTranslation * MoveSpeed - V * Drag) * dt; - XMStoreFloat3(&mVelocity, V); + XMStoreFloat3(&this->mpCamera->mVelocity, V); + + this->mpCamera->UpdateViewMatrix(); + + // move based on velocity + XMVECTOR P = XMLoadFloat3(&this->mpCamera->mPosition); + P += V * dt; + XMStoreFloat3(&this->mpCamera->mPosition, P); +} + +CameraController* FirstPersonController::Clone_impl(Camera* pNewCam) +{ + FirstPersonController* p = new FirstPersonController(pNewCam); + p->AngularSpeedDeg = this->AngularSpeedDeg; + p->Drag = this->Drag; + p->MoveSpeed = this->MoveSpeed; + return p; } diff --git a/Source/Application/Camera.h b/Source/Application/Camera.h index f58d19b7..80e1a6bf 100644 --- a/Source/Application/Camera.h +++ b/Source/Application/Camera.h @@ -23,93 +23,38 @@ #include "Math.h" #include +#include -struct FFrustumPlaneset -{ // plane equations: aX + bY + cZ + d = 0 - DirectX::XMFLOAT4 abcd[6]; // r, l, t, b, n, f - enum EPlaneset - { - PL_RIGHT = 0, - PL_LEFT, - PL_TOP, - PL_BOTTOM, - PL_FAR, - PL_NEAR - }; - - // src: http://gamedevs.org/uploads/fast-extraction-viewing-frustum-planes-from-world-view-projection-matrix.pdfe - // gets the frustum planes based on @projectionTransformation. if: - // - // - @projectionTransformation is proj matrix -> view space plane equations - // - @projectionTransformation is viewProj matrix -> world space plane equations - // - @projectionTransformation is worldViewProj matrix -> model space plane equations - // - inline static FFrustumPlaneset ExtractFromMatrix(const DirectX::XMMATRIX& projectionTransformation) - { - const DirectX::XMMATRIX& m = projectionTransformation; - - FFrustumPlaneset viewPlanes; - // TODO: XMVECTOR impl; - viewPlanes.abcd[FFrustumPlaneset::PL_RIGHT] = DirectX::XMFLOAT4( - m.r[0].m128_f32[3] - m.r[0].m128_f32[0], - m.r[1].m128_f32[3] - m.r[1].m128_f32[0], - m.r[2].m128_f32[3] - m.r[2].m128_f32[0], - m.r[3].m128_f32[3] - m.r[3].m128_f32[0] - ); - viewPlanes.abcd[FFrustumPlaneset::PL_LEFT] = DirectX::XMFLOAT4( - m.r[0].m128_f32[3] + m.r[0].m128_f32[0], - m.r[1].m128_f32[3] + m.r[1].m128_f32[0], - m.r[2].m128_f32[3] + m.r[2].m128_f32[0], - m.r[3].m128_f32[3] + m.r[3].m128_f32[0] - ); - viewPlanes.abcd[FFrustumPlaneset::PL_TOP] = DirectX::XMFLOAT4( - m.r[0].m128_f32[3] - m.r[0].m128_f32[1], - m.r[1].m128_f32[3] - m.r[1].m128_f32[1], - m.r[2].m128_f32[3] - m.r[2].m128_f32[1], - m.r[3].m128_f32[3] - m.r[3].m128_f32[1] - ); - viewPlanes.abcd[FFrustumPlaneset::PL_BOTTOM] = DirectX::XMFLOAT4( - m.r[0].m128_f32[3] + m.r[0].m128_f32[1], - m.r[1].m128_f32[3] + m.r[1].m128_f32[1], - m.r[2].m128_f32[3] + m.r[2].m128_f32[1], - m.r[3].m128_f32[3] + m.r[3].m128_f32[1] - ); - viewPlanes.abcd[FFrustumPlaneset::PL_FAR] = DirectX::XMFLOAT4( - m.r[0].m128_f32[3] - m.r[0].m128_f32[2], - m.r[1].m128_f32[3] - m.r[1].m128_f32[2], - m.r[2].m128_f32[3] - m.r[2].m128_f32[2], - m.r[3].m128_f32[3] - m.r[3].m128_f32[2] - ); - viewPlanes.abcd[FFrustumPlaneset::PL_NEAR] = DirectX::XMFLOAT4( - m.r[0].m128_f32[2], - m.r[1].m128_f32[2], - m.r[2].m128_f32[2], - m.r[3].m128_f32[2] - ); - return viewPlanes; - } -}; +class Camera; +class Input; -struct FCameraData -{ - float x, y, z; // position - float width; - float height; - float nearPlane; - float farPlane; - float fovV_Degrees; - float yaw, pitch; // in degrees - bool bPerspectiveProjection; -}; -struct ProjectionMatrixParameters + + +// --------------------------------------------------------- +// DATA +// --------------------------------------------------------- +struct FProjectionMatrixParameters { - float ViewporWidth; - float ViewporHeight; + float ViewportWidth; // needed for orthographic projection + float ViewportHeight; // needed for orthographic projection float NearZ; float FarZ; float FieldOfView; bool bPerspectiveProjection; }; +struct FCameraParameters +{ + float x, y, z; // position + float Yaw, Pitch; // in degrees + + FProjectionMatrixParameters ProjectionParams; + + bool bInitializeCameraController; + bool bFirstPerson; // First Person / orbit + float TranslationSpeed; + float AngularSpeed; + float Drag; +}; struct FCameraInput { @@ -120,35 +65,90 @@ struct FCameraInput std::array DeltaMouseXY; }; + + +// --------------------------------------------------------- +// CAMERA CONTROLLERS +// --------------------------------------------------------- +class CameraController +{ +public: + virtual void UpdateCamera(const Input& input, float dt) = 0; + inline std::unique_ptr Clone(Camera* pNewCam) { return std::unique_ptr(Clone_impl(pNewCam)); } +protected: + virtual CameraController* Clone_impl(Camera* pNewCam) = 0; + + CameraController() = delete; + CameraController(Camera* pCamera) : mpCamera(pCamera) {} + +protected: + Camera* mpCamera = nullptr; +}; +class FirstPersonController : public CameraController +{ +public: + FirstPersonController() = delete; + FirstPersonController(Camera* pCam, float moveSpeed = 1000.0f, float angularSpeed = 0.05f, float drag = 9.5f); + void UpdateCamera(const Input& input, float dt) override; +protected: + CameraController* Clone_impl(Camera* pNewCam) override; +private: + float Drag; + float AngularSpeedDeg; + float MoveSpeed; +}; +class OrbitController : public CameraController +{ +public: + OrbitController(Camera* pCam); + void UpdateCamera(const Input& input, float dt) override; +protected: + CameraController* Clone_impl(Camera* pNewCam) override; +private: + DirectX::XMFLOAT3 mF3LookAt; +}; + + + +// --------------------------------------------------------- +// CAMERA +// --------------------------------------------------------- class Camera { + friend class OrbitController; + friend class FirstPersonController; public: Camera(); ~Camera(void); + Camera(Camera&& other); + //Camera(const Camera& other); + Camera Clone(); - void InitializeCamera(const FCameraData& data); + void InitializeCamera(const FCameraParameters& data); + void InitializeController(bool bFirstPersonController, const FCameraParameters& data); - void SetProjectionMatrix(const ProjectionMatrixParameters& params); - void UpdateViewMatrix(); + void SetProjectionMatrix(const FProjectionMatrixParameters& params); - // updates View Matrix @mMatView - void Update(const float dt, const FCameraInput& input); + void UpdateViewMatrix(); + inline void Update(float dt, const Input& input) { if(pController) pController->UpdateCamera(input, dt); } + inline float GetYaw() const { return mYaw; } + inline float GetPitch() const { return mPitch; } DirectX::XMFLOAT3 GetPositionF() const; DirectX::XMMATRIX GetViewMatrix() const; DirectX::XMMATRIX GetViewInverseMatrix() const; DirectX::XMMATRIX GetProjectionMatrix() const; DirectX::XMMATRIX GetRotationMatrix() const; + inline const FProjectionMatrixParameters& GetProjectionParameters() const { return mProjParams; } + inline FProjectionMatrixParameters& GetProjectionParameters() { return mProjParams; } // returns World Space frustum plane set FFrustumPlaneset GetViewFrustumPlanes() const; void SetPosition(float x, float y, float z); - void Rotate(float yaw, float pitch, const float dt); - void Move(const float dt, const FCameraInput& input); - void Rotate(const float dt, const FCameraInput& input); - + void Rotate(float yaw, float pitch); +private: //-------------------------- DirectX::XMFLOAT3 mPosition; float mYaw = 0.0f; @@ -156,14 +156,11 @@ class Camera DirectX::XMFLOAT3 mVelocity; float mPitch = 0.0f; // ------------------------- - ProjectionMatrixParameters mProjParams; - // ------------------------- - float Drag; - float AngularSpeedDeg; - float MoveSpeed; + FProjectionMatrixParameters mProjParams; // ------------------------- DirectX::XMFLOAT4X4 mMatProj; // ------------------------- DirectX::XMFLOAT4X4 mMatView; // ------------------------- + std::unique_ptr pController; }; diff --git a/Source/Application/FileParser.cpp b/Source/Application/FileParser.cpp new file mode 100644 index 00000000..2436fed1 --- /dev/null +++ b/Source/Application/FileParser.cpp @@ -0,0 +1,660 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#include "VQEngine.h" + +#include "Libs/VQUtils/Source/utils.h" +#include "Libs/VQUtils/Libs/tinyxml2/tinyxml2.h" + +#include +#include + +#ifdef _DEBUG +constexpr char* BUILD_CONFIG = "-Debug"; +#else +constexpr char* BUILD_CONFIG = ""; +#endif +constexpr char* VQENGINE_VERSION = "v0.5.0"; + + +static std::pair ParseLineINI(const std::string& iniLine, bool* pbSectionTag) +{ + assert(!iniLine.empty()); + std::pair SettingNameValuePair; + + const bool bSectionTag = iniLine[0] == '['; + if(pbSectionTag) + *pbSectionTag = bSectionTag; + + if (bSectionTag) + { + auto vecSettingNameValue = StrUtil::split(iniLine.substr(1), ']'); + SettingNameValuePair.first = vecSettingNameValue[0]; + } + else + { + auto vecSettingNameValue = StrUtil::split(iniLine, '='); + assert(vecSettingNameValue.size() >= 2); + SettingNameValuePair.first = vecSettingNameValue[0]; + SettingNameValuePair.second = vecSettingNameValue[1]; + } + + return SettingNameValuePair; +} + +static std::unordered_map S_LOOKUP_STR_TO_DISPLAYMODE = +{ + { "Fullscreen" , EDisplayMode::EXCLUSIVE_FULLSCREEN } + , { "Borderless" , EDisplayMode::BORDERLESS_FULLSCREEN } + , { "BorderlessFullscreen" , EDisplayMode::BORDERLESS_FULLSCREEN } + , { "BorderlessWindowed" , EDisplayMode::BORDERLESS_FULLSCREEN } + , { "Windowed" , EDisplayMode::WINDOWED } +}; + + + +FStartupParameters VQEngine::ParseEngineSettingsFile() +{ + constexpr char* ENGINE_SETTINGS_FILE_NAME = "Data/EngineSettings.ini"; + FStartupParameters params = {}; + + std::ifstream file(ENGINE_SETTINGS_FILE_NAME); + if (file.is_open()) + { + std::string line; + std::string currSection; + bool bReadingSection = false; + while (std::getline(file, line)) + { + if (line[0] == ';') continue; // skip comment lines + if (line == "") continue; // skip empty lines + + const std::pair SettingNameValuePair = ParseLineINI(line, &bReadingSection); + const std::string& SettingName = SettingNameValuePair.first; + const std::string& SettingValue = SettingNameValuePair.second; + + // Header sections; + if (bReadingSection) + { + currSection = SettingName; + continue; + } + + + // + // Graphics + // + if (SettingName == "VSync") + { + params.bOverrideGFXSetting_bVSync = true; + params.EngineSettings.gfx.bVsync = StrUtil::ParseBool(SettingValue); + } + if (SettingName == "RenderScale") + { + params.bOverrideGFXSetting_RenderScale = true; + params.EngineSettings.gfx.RenderScale = StrUtil::ParseFloat(SettingValue); + } + if (SettingName == "TripleBuffer") + { + params.bOverrideGFXSetting_bUseTripleBuffering = true; + params.EngineSettings.gfx.bUseTripleBuffering = StrUtil::ParseBool(SettingValue); + } + if (SettingName == "AntiAliasing" || SettingName == "AA") + { + params.bOverrideGFXSetting_bAA = true; + params.EngineSettings.gfx.bAntiAliasing = StrUtil::ParseBool(SettingValue); + } + if (SettingName == "MaxFrameRate" || SettingName == "MaxFPS") + { + params.bOverrideGFXSetting_bMaxFrameRate = true; + if (SettingValue == "Unlimited" || SettingValue == "0") + params.EngineSettings.gfx.MaxFrameRate = 0; + else if (SettingValue == "Auto" || SettingValue == "Automatic" || SettingValue == "-1") + params.EngineSettings.gfx.MaxFrameRate = -1; + else + params.EngineSettings.gfx.MaxFrameRate = StrUtil::ParseInt(SettingValue); + } + + // + // Engine + // + if (SettingName == "Width") + { + params.bOverrideENGSetting_MainWindowWidth = true; + params.EngineSettings.WndMain.Width = StrUtil::ParseInt(SettingValue); + } + if (SettingName == "Height") + { + params.bOverrideENGSetting_MainWindowHeight = true; + params.EngineSettings.WndMain.Height = StrUtil::ParseInt(SettingValue); + } + if (SettingName == "DisplayMode") + { + if (S_LOOKUP_STR_TO_DISPLAYMODE.find(SettingValue) != S_LOOKUP_STR_TO_DISPLAYMODE.end()) + { + params.bOverrideENGSetting_bDisplayMode = true; + params.EngineSettings.WndMain.DisplayMode = S_LOOKUP_STR_TO_DISPLAYMODE.at(SettingValue); + } + } + if (SettingName == "PreferredDisplay") + { + params.bOverrideENGSetting_PreferredDisplay = true; + params.EngineSettings.WndMain.PreferredDisplay = StrUtil::ParseInt(SettingValue); + } + if (SettingName == "HDR") + { + params.bOverrideGFXSetting_bHDR = true; + params.EngineSettings.WndMain.bEnableHDR = StrUtil::ParseBool(SettingValue); + } + + + if (SettingName == "DebugWindow") + { + params.bOverrideENGSetting_bDebugWindowEnable = true; + params.EngineSettings.bShowDebugWindow = StrUtil::ParseBool(SettingValue); + } + if (SettingName == "DebugWindowWidth") + { + params.bOverrideENGSetting_DebugWindowWidth = true; + params.EngineSettings.WndDebug.Width = StrUtil::ParseInt(SettingValue); + } + if (SettingName == "DebugWindowHeight") + { + params.bOverrideENGSetting_DebugWindowHeight = true; + params.EngineSettings.WndDebug.Height = StrUtil::ParseInt(SettingValue); + } + if (SettingName == "DebugWindowDisplayMode") + { + if (S_LOOKUP_STR_TO_DISPLAYMODE.find(SettingValue) != S_LOOKUP_STR_TO_DISPLAYMODE.end()) + { + params.bOverrideENGSetting_DebugWindowDisplayMode = true; + params.EngineSettings.WndDebug.DisplayMode = S_LOOKUP_STR_TO_DISPLAYMODE.at(SettingValue); + } + } + if (SettingName == "DebugWindowPreferredDisplay") + { + params.bOverrideENGSetting_DebugWindowPreferredDisplay = true; + params.EngineSettings.WndDebug.PreferredDisplay = StrUtil::ParseInt(SettingValue); + } + + if (SettingName == "Scene") + { + params.bOverrideENGSetting_StartupScene = true; + params.EngineSettings.StartupScene = SettingValue; + } + + } + } + else + { + Log::Warning("Cannot find settings file %s in current directory: %s", ENGINE_SETTINGS_FILE_NAME, DirectoryUtil::GetCurrentPath().c_str()); + Log::Warning("Will use default settings for Engine & Graphics.", ENGINE_SETTINGS_FILE_NAME, DirectoryUtil::GetCurrentPath().c_str()); + } + + file.close(); + + return params; +} + +std::vector> VQEngine::ParseSceneIndexMappingFile() +{ + constexpr char* SCENE_MAPPING_FILE_NAME = "Data/Scenes.ini"; + + std::vector> SceneIndexMappings; + + std::ifstream file(SCENE_MAPPING_FILE_NAME); + if (file.is_open()) + { + std::string line; + std::string currSection; + bool bReadingSection = false; + while (std::getline(file, line)) + { + if (line[0] == ';') continue; // skip comment lines + if (line == "") continue; // skip empty lines + + const std::pair SettingNameValuePair = ParseLineINI(line, &bReadingSection); + const std::string& SettingName = SettingNameValuePair.first; + const std::string& SettingValue = SettingNameValuePair.second; + + // Header sections; + if (bReadingSection) + { + currSection = SettingName; + continue; + } + + const int SceneIndex = StrUtil::ParseInt(SettingValue); + const std::string& SceneName = SettingName; + SceneIndexMappings.push_back(std::make_pair(SceneName, SceneIndex)); + } + } + else + { + Log::Warning("Cannot find settings file %s in current directory: %s", SCENE_MAPPING_FILE_NAME, DirectoryUtil::GetCurrentPath().c_str()); + Log::Warning("Default scene mapping will be used."); + } + + std::sort(SceneIndexMappings.begin(), SceneIndexMappings.end(), [](const auto& l, const auto& r) { return l.second < r.second; }); + + return SceneIndexMappings; +} + +std::vector VQEngine::ParseEnvironmentMapsFile() +{ + constexpr char* SETTINGS_FILE_NAME = "Data/EnvironmentMaps.ini"; + + std::vector EnvironmentMapDescriptors; + + std::ifstream file(SETTINGS_FILE_NAME); + if (file.is_open()) + { + std::string line; + bool bReadingSection = false; + bool bRecentlyReadEmptyLine = false; + bool bCurrentlyReadingEnvMap = false; + FEnvironmentMapDescriptor desc = {}; + while (std::getline(file, line)) + { + if (line[0] == ';') continue; // skip comment lines + if (line == "") { bRecentlyReadEmptyLine = true; continue; } // skip empty lines + + const std::pair SettingNameValuePair = ParseLineINI(line, &bReadingSection); + const std::string& SettingName = SettingNameValuePair.first; + const std::string& SettingValue = SettingNameValuePair.second; + + // Header sections; + if (bReadingSection) + { + bCurrentlyReadingEnvMap = true; + if (bRecentlyReadEmptyLine) + { + EnvironmentMapDescriptors.push_back(desc); + desc = {}; + bRecentlyReadEmptyLine = false; + } + desc.Name = SettingName; + continue; + } + + if (SettingName == "Path") + { + desc.FilePath = SettingValue; + } + if (SettingName == "MaxCLL") + { + desc.MaxContentLightLevel = StrUtil::ParseFloat(SettingValue); + } + bRecentlyReadEmptyLine = false; + } + if (bCurrentlyReadingEnvMap) + { + EnvironmentMapDescriptors.push_back(desc); + desc = {}; + bCurrentlyReadingEnvMap = false; + } + } + else + { + Log::Error("Cannot find settings file %s in current directory: %s", SETTINGS_FILE_NAME, DirectoryUtil::GetCurrentPath().c_str()); + } + + return EnvironmentMapDescriptors; +} + +std::vector VQEngine::ParseHDRProfilesFile() +{ + constexpr char* SETTINGS_FILE_NAME = "Data/HDRDisplayProfiles.ini"; + + std::vector HDRProfiles; + + std::ifstream file(SETTINGS_FILE_NAME); + if (file.is_open()) + { + std::string line; + bool bReadingSection = false; // Section is an .ini term + bool bRecentlyReadEmptyLine = false; + bool bCurrentlyReadingProfile = false; + FDisplayHDRProfile profile = {}; + while (std::getline(file, line)) + { + if (line[0] == ';') continue; // skip comment lines + if (line == "") { bRecentlyReadEmptyLine = true; continue; } // skip empty lines + + const std::pair SettingNameValuePair = ParseLineINI(line, &bReadingSection); + const std::string& SettingName = SettingNameValuePair.first; + const std::string& SettingValue = SettingNameValuePair.second; + + // Header sections; + if (bReadingSection) + { + bCurrentlyReadingProfile = true; + if (bRecentlyReadEmptyLine) + { + HDRProfiles.push_back(profile); + profile = {}; + bRecentlyReadEmptyLine = false; + } + profile.DisplayName = SettingName; + continue; + } + + if (SettingName == "MaxBrightness") + { + profile.MaxBrightness = StrUtil::ParseFloat(SettingValue); + } + if (SettingName == "MinBrightness") + { + profile.MinBrightness = StrUtil::ParseFloat(SettingValue); + } + bRecentlyReadEmptyLine = false; + } + if (bCurrentlyReadingProfile) // Take into account the last item we're reading (.push_back() isn't called above for the last item) + { + HDRProfiles.push_back(profile); + profile = {}; + bCurrentlyReadingProfile = false; + } + } + else + { + Log::Error("Cannot find settings file %s in current directory: %s", SETTINGS_FILE_NAME, DirectoryUtil::GetCurrentPath().c_str()); + } + return HDRProfiles; +} + +FSceneRepresentation VQEngine::ParseSceneFile(const std::string& SceneFile) +{ + using namespace DirectX; + using namespace tinyxml2; + //----------------------------------------------------------------- + constexpr char* XML_TAG__SCENE = "Scene"; + constexpr char* XML_TAG__ENVIRONMENT_MAP = "EnvironmentMap"; + constexpr char* XML_TAG__ENVIRONMENT_MAP_PRESET = "Preset"; + constexpr char* XML_TAG__CAMERA = "Camera"; + constexpr char* XML_TAG__GAMEOBJECT = "GameObject"; + constexpr char* XML_TAG__MATERIAL = "Material"; + //----------------------------------------------------------------- + constexpr char* SCENE_FILES_DIRECTORY = "Data/Levels/"; + //----------------------------------------------------------------- + + // parse vectors -------------------------------------------------- + // e.g." 0.0 9 -1.0f" -> [0.0f, 9.0f, -1.0f] + auto fnParseF3 = [](const std::string& xyz) -> XMFLOAT3 + { + XMFLOAT3 f3; + std::vector tokens = StrUtil::split(xyz, ' '); + assert(tokens.size() == 3); + f3.x = StrUtil::ParseFloat(tokens[0]); + f3.y = StrUtil::ParseFloat(tokens[1]); + f3.z = StrUtil::ParseFloat(tokens[2]); + return f3; + }; + auto fnParseF4 = [](const std::string& xyzw) -> XMFLOAT4 + { + XMFLOAT4 f4; + std::vector tokens = StrUtil::split(xyzw, ' '); + assert(tokens.size() == 4); + f4.x = StrUtil::ParseFloat(tokens[0]); + f4.y = StrUtil::ParseFloat(tokens[1]); + f4.z = StrUtil::ParseFloat(tokens[2]); + f4.w = StrUtil::ParseFloat(tokens[3]); + return f4; + }; + // parse xml elements --------------------------------------------- + auto fnParseXMLStringVal = [](XMLElement* pEle, std::string& dest) + { + XMLNode* pNode = pEle->FirstChild(); + if (pNode) + { + dest = pNode->Value(); + } + }; + auto fnParseXMLFloatVal = [](XMLElement* pEle, float& dest) + { + XMLNode* pNode = pEle->FirstChild(); + if (pNode) + { + dest = StrUtil::ParseFloat(pNode->Value()); + } + }; + auto fnParseXMLFloat3Val = [&](XMLElement* pEle, XMFLOAT3& f3) + { + XMLNode* pNode = pEle->FirstChild(); + if (pNode) + { + f3 = fnParseF3(pNode->Value()); + } + }; + auto fnParseXMLFloat4Val = [&](XMLElement* pEle, XMFLOAT4& f4) + { + XMLNode* pNode = pEle->FirstChild(); + if (pNode) + { + f4 = fnParseF4(pNode->Value()); + } + }; + // parse engine stuff ------------------------------------------- + auto fnParseTransform = [&](XMLElement* pTransform) -> Transform + { + Transform tf; + + XMLElement* pPos = pTransform->FirstChildElement("Position"); + XMLElement* pQuat = pTransform->FirstChildElement("Quaternion"); + XMLElement* pRot = pTransform->FirstChildElement("Rotation"); + XMLElement* pScl = pTransform->FirstChildElement("Scale"); + if (pPos) fnParseXMLFloat3Val(pPos, tf._position); + if (pScl) fnParseXMLFloat3Val(pScl, tf._scale); + if (pQuat) + { + XMFLOAT4 qf4; fnParseXMLFloat4Val(pQuat, qf4); + tf._rotation = Quaternion(qf4.w, XMFLOAT3(qf4.x, qf4.y, qf4.z)); + } + if (pRot) + { + XMFLOAT3 f3; fnParseXMLFloat3Val(pRot, f3); + tf.RotateAroundGlobalXAxisDegrees(f3.x); + tf.RotateAroundGlobalYAxisDegrees(f3.y); + tf.RotateAroundGlobalZAxisDegrees(f3.z); + } + return tf; + }; + auto fnParseMaterial = [&](XMLElement* pMat) -> FMaterialRepresentation + { + FMaterialRepresentation mat; + + XMLElement* pName = pMat->FirstChildElement("Name"); + XMLElement* pDiff = pMat->FirstChildElement("Diffuse"); + XMLElement* pAlph = pMat->FirstChildElement("Alpha"); + XMLElement* pEmsv = pMat->FirstChildElement("Emissive"); + XMLElement* pEmsI = pMat->FirstChildElement("EmissiveIntensity"); + XMLElement* pRgh = pMat->FirstChildElement("Roughness"); + XMLElement* pMtl = pMat->FirstChildElement("Metalness"); + //------------------------------------------------------------------ + XMLElement* pDiffMap = pMat->FirstChildElement("DiffuseMap"); + XMLElement* pNrmlMap = pMat->FirstChildElement("NormalMap"); + XMLElement* pEmsvMap = pMat->FirstChildElement("EmissiveMap"); + XMLElement* pAlphMap = pMat->FirstChildElement("AlphaMaskMap"); + XMLElement* pMtlMap = pMat->FirstChildElement("MetallicMap"); + XMLElement* pRghMap = pMat->FirstChildElement("RoughnessMap"); + + if (pName) fnParseXMLStringVal(pName, mat.Name); + if (pDiff) fnParseXMLFloat3Val(pDiff, mat.DiffuseColor); + if (pAlph) fnParseXMLFloatVal(pAlph, mat.Alpha); + if (pEmsv) fnParseXMLFloat3Val(pEmsI, mat.EmissiveColor); + if (pEmsI) fnParseXMLFloatVal(pEmsI, mat.EmissiveIntensity); + if (pRgh ) fnParseXMLFloatVal(pRgh , mat.Roughness); + if (pMtl ) fnParseXMLFloatVal(pMtl , mat.Metalness); + //------------------------------------------------------------------- + if (pDiffMap) fnParseXMLStringVal(pDiffMap, mat.DiffuseMapFilePath ); + if (pNrmlMap) fnParseXMLStringVal(pNrmlMap, mat.NormalMapFilePath ); + if (pEmsvMap) fnParseXMLStringVal(pEmsvMap, mat.EmissiveMapFilePath ); + if (pAlphMap) fnParseXMLStringVal(pAlphMap, mat.AlphaMaskMapFilePath); + if (pMtlMap ) fnParseXMLStringVal(pMtlMap , mat.MetallicMapFilePath ); + if (pRghMap ) fnParseXMLStringVal(pRghMap , mat.RoughnessMapFilePath); + + return mat; + }; + //----------------------------------------------------------------- + + // Start reading scene XML file + FSceneRepresentation SceneRep = {}; + + // parse XML + tinyxml2::XMLDocument doc; + doc.LoadFile(SceneFile.c_str()); + + // scene name + SceneRep.SceneName = DirectoryUtil::GetFileNameWithoutExtension(SceneFile); + + + XMLElement* pScene = doc.FirstChildElement(XML_TAG__SCENE); + if (pScene) + { + XMLElement* pCurrentSceneElement = pScene->FirstChildElement(); + if (!pCurrentSceneElement) + { + return SceneRep; + } + + do + { + // Environment Map + const std::string CurrEle = pCurrentSceneElement->Value(); + if (XML_TAG__ENVIRONMENT_MAP == CurrEle) + { + XMLElement* pPreset = pCurrentSceneElement->FirstChildElement(XML_TAG__ENVIRONMENT_MAP_PRESET); + if (pPreset) + { + fnParseXMLStringVal(pPreset, SceneRep.EnvironmentMapPreset); + } + } + + // Cameras + else if (XML_TAG__CAMERA == CurrEle) + { + FCameraParameters cam = {}; + XMLElement*& pCam = pCurrentSceneElement; + + // transform + XMLElement* pPos = pCam->FirstChildElement("Position"); + XMLElement* pPitch = pCam->FirstChildElement("Pitch"); + XMLElement* pYaw = pCam->FirstChildElement("Yaw"); + + // projection + XMLElement* pProj = pCam->FirstChildElement("Projection"); + XMLElement* pFoV = pCam->FirstChildElement("FoV"); + XMLElement* pNear = pCam->FirstChildElement("Near"); + XMLElement* pFar = pCam->FirstChildElement("Far"); + + // attributes + XMLElement* pFP = pCam->FirstChildElement("FirstPerson"); + XMLElement* pTSpeed = pFP ? pFP->FirstChildElement("TranslationSpeed") : nullptr; + XMLElement* pASpeed = pFP ? pFP->FirstChildElement("AngularSpeed") : nullptr; + XMLElement* pDrag = pFP ? pFP->FirstChildElement("Drag") : nullptr; + XMLElement* pOrbit = pCam->FirstChildElement("Orbit"); + + // transform ---------------------------------------- + if (pPos) + { + XMFLOAT3 xyz; + fnParseXMLFloat3Val(pPos, xyz); + cam.x = xyz.x; + cam.y = xyz.y; + cam.z = xyz.z; + } + if (pPitch) fnParseXMLFloatVal(pPitch, cam.Pitch); + if (pYaw) fnParseXMLFloatVal(pYaw, cam.Yaw); + + // projection---------------------------------------- + if(pProj) + { + std::string projVal; + fnParseXMLStringVal(pProj, projVal); + cam.ProjectionParams.bPerspectiveProjection = projVal == "Perspective"; + } + if(pFoV ) fnParseXMLFloatVal(pFoV , cam.ProjectionParams.FieldOfView); + if(pNear) fnParseXMLFloatVal(pNear, cam.ProjectionParams.NearZ); + if(pFar ) fnParseXMLFloatVal(pFar , cam.ProjectionParams.FarZ); + + + // attributes---------------------------------------- + if (pFP) + { + cam.bInitializeCameraController = true; + cam.bFirstPerson = true; + if(pTSpeed) fnParseXMLFloatVal(pTSpeed, cam.TranslationSpeed); + if(pASpeed) fnParseXMLFloatVal(pASpeed, cam.AngularSpeed); + if(pDrag ) fnParseXMLFloatVal(pDrag , cam.Drag); + } + if (pOrbit) + { + cam.bInitializeCameraController = true; + cam.bFirstPerson = false; + + } + + SceneRep.Cameras.push_back(cam); + } + + // Materials + else if (XML_TAG__MATERIAL == CurrEle) + { + FMaterialRepresentation mat = fnParseMaterial(pCurrentSceneElement); + SceneRep.Materials.push_back(mat); + } + + // Game Objects + else if (XML_TAG__GAMEOBJECT == CurrEle) + { + FGameObjectRepresentation obj; + + XMLElement*& pObj = pCurrentSceneElement; + XMLElement* pTransform = pObj->FirstChildElement("Transform"); + XMLElement* pModel = pObj->FirstChildElement("Model"); + + // Transform + if (pTransform) + { + obj.tf = fnParseTransform(pTransform); + } + + // Model + if (pModel) + { + XMLElement* pMesh = pModel->FirstChildElement("Mesh"); + XMLElement* pMaterial = pModel->FirstChildElement("MaterialName"); + XMLElement* pModelPath = pModel->FirstChildElement("Path"); + XMLElement* pModelName = pModel->FirstChildElement("Name"); + + if (pMesh) fnParseXMLStringVal(pMesh, obj.BuiltinMeshName); + if (pMaterial) fnParseXMLStringVal(pMaterial, obj.MaterialName); + if (pModelPath) fnParseXMLStringVal(pModelPath, obj.ModelFilePath); + if (pModelName) fnParseXMLStringVal(pModelName, obj.ModelName); + } + + SceneRep.Objects.push_back(obj); + } + + + pCurrentSceneElement = pCurrentSceneElement->NextSiblingElement(); + } while (pCurrentSceneElement); + } // if (pScene) + + return SceneRep; +} diff --git a/Source/Application/GPUMarker.cpp b/Source/Application/GPUMarker.cpp new file mode 100644 index 00000000..2e5750a8 --- /dev/null +++ b/Source/Application/GPUMarker.cpp @@ -0,0 +1,40 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#include +#include "GPUMarker.h" + +// https://devblogs.microsoft.com/pix/winpixeventruntime/#:~:text=An%20%E2%80%9Cevent%E2%80%9D%20represents%20a%20region,a%20single%20point%20in%20time. +// https://devblogs.microsoft.com/pix/pix-2008-26-new-capture-layer/ + +ScopedGPUMarker::ScopedGPUMarker(ID3D12GraphicsCommandList* pCmdList, const char* pLabel, unsigned PIXColor) + : mpCmdList(pCmdList) +{ + PIXBeginEvent(mpCmdList, (unsigned long long)PIXColor, pLabel); +} + +ScopedGPUMarker::ScopedGPUMarker(ID3D12CommandQueue* pCmdQueue, const char* pLabel, unsigned PIXColor) + :mpCmdQueue(pCmdQueue) +{ + PIXBeginEvent(mpCmdQueue, PIXColor, pLabel); +} + +ScopedGPUMarker::~ScopedGPUMarker() +{ + PIXEndEvent(mpCmdList); +} diff --git a/Source/Application/GPUMarker.h b/Source/Application/GPUMarker.h new file mode 100644 index 00000000..30dde0b8 --- /dev/null +++ b/Source/Application/GPUMarker.h @@ -0,0 +1,44 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#pragma once + +#include "WinPixEventRuntime/pix3.h" + +#define SCOPED_GPU_MARKER(pCmd, pStr) ScopedGPUMarker GPUMarker(pCmd,pStr) + +struct ID3D12GraphicsCommandList; +struct ID3D12CommandQueue; + +class ScopedGPUMarker +{ +public: + ScopedGPUMarker(ID3D12GraphicsCommandList* pCmdList, const char* pLabel, unsigned PIXColor = PIX_COLOR_DEFAULT); + ScopedGPUMarker(ID3D12CommandQueue* pCmdQueue, const char* pLabel, unsigned PIXColor = PIX_COLOR_DEFAULT); + ~ScopedGPUMarker(); + + ScopedGPUMarker(const ScopedGPUMarker&) = delete; + ScopedGPUMarker(ScopedGPUMarker&&) = delete; + ScopedGPUMarker& operator=(ScopedGPUMarker&&) = delete; +private: + union + { + ID3D12GraphicsCommandList* mpCmdList; + ID3D12CommandQueue* mpCmdQueue; + }; +}; \ No newline at end of file diff --git a/Source/Application/GameObject.cpp b/Source/Application/GameObject.cpp new file mode 100644 index 00000000..add3aa36 --- /dev/null +++ b/Source/Application/GameObject.cpp @@ -0,0 +1,19 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#include "GameObject.h" \ No newline at end of file diff --git a/Source/Application/GameObject.h b/Source/Application/GameObject.h new file mode 100644 index 00000000..8922af84 --- /dev/null +++ b/Source/Application/GameObject.h @@ -0,0 +1,35 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#pragma once + +#include "Types.h" + +#include + +struct BoundingBox {}; +class GameObject +{ +public: + TransformID mTransformID = INVALID_ID; + ModelID mModelID = INVALID_ID; + + // Todo: move to scene and rename to @BoundingBoxes_GameObjects; + BoundingBox mBoundingBox; + std::vector mMeshBoundingBoxes; +}; diff --git a/Source/Application/Light.cpp b/Source/Application/Light.cpp new file mode 100644 index 00000000..41b8e9f8 --- /dev/null +++ b/Source/Application/Light.cpp @@ -0,0 +1,21 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#include "Light.h" + +// TBA \ No newline at end of file diff --git a/Source/Application/Light.h b/Source/Application/Light.h new file mode 100644 index 00000000..8a571c61 --- /dev/null +++ b/Source/Application/Light.h @@ -0,0 +1,75 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#pragma once + + +#include "Transform.h" +#include "Settings.h" +//#include "DataStructures.h" +// +//#include "Renderer/RenderingEnums.h" +//#include "Renderer/Texture.h" +// +//#include "Utilities/Color.h" + +#include + + +// Only used for point lights when querying LightSpaceMatrix, ViewMatrix and ViewFrustumPlanes. +// +#define DEFAULT_POINT_LIGHT_LOOK_DIRECTION Texture::CubemapUtility::ECubeMapLookDirections::CUBEMAP_LOOK_FRONT + + +// Design considerations here: +// +// INHERITANCE +// - if we were to use inheritance for different types of lights, then we can utilize pure virtual functions +// to enforce class-specific behavior. However, now, we cannot store a vector due to pure virtuality. +// most likely solution is the store pointers to derived types, which now requires a memory manager for lights +// if we want to iterate over lights in a linear-memory-access fashion. +// +// C-STYLE +// - instead, we can collect the light-specific data under a union and enforce light-specific behavior +// through the ELightType enum. Currently favoring this approach over inheritance to avoid maintaining the memory +// of the derived types and simply making use of a vector to hold all light data. +// + +struct Light +{ + enum ELightType : int + { + POINT = 0, + SPOT, + DIRECTIONAL, + + LIGHT_TYPE_COUNT + }; + + // returns a view matrix for each light type that supports shadow mapping + // + static DirectX::XMMATRIX CalculateDirectionalLightViewMatrix(const Light& mDirLight); + static DirectX::XMMATRIX CalculateSpotLightViewMatrix(const Transform& mTransform); +#if 0 + static DirectX::XMMATRIX CalculatePointLightViewMatrix(Texture::CubemapUtility::ECubeMapLookDirections lookDir, const vec3& position); +#endif + + // TODO +}; + +//constexpr size_t SZ_LIGHT_STRUCT = sizeof(Light); diff --git a/Source/Application/Main.cpp b/Source/Application/Main.cpp index 1181a92a..ba4db388 100644 --- a/Source/Application/Main.cpp +++ b/Source/Application/Main.cpp @@ -16,9 +16,12 @@ // // Contact: volkanilbeyli@gmail.com +#include +#include +#pragma comment(lib, "shcore.lib") + #include #include -#include #include #include @@ -29,7 +32,6 @@ #include "Platform.h" #include "VQEngine.h" - void ParseCommandLineParameters(FStartupParameters& refStartupParams, PSTR pScmdl) { const std::string StrCmdLineParams = pScmdl; @@ -158,12 +160,20 @@ void ParseCommandLineParameters(FStartupParameters& refStartupParams, PSTR pScmd else refStartupParams.EngineSettings.gfx.MaxFrameRate = StrUtil::ParseInt(paramValue); } + + if (paramName == "-Scene") + { + refStartupParams.bOverrideENGSetting_StartupScene = true; + refStartupParams.EngineSettings.StartupScene = paramValue; + } } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR pScmdl, int iCmdShow) { + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + FStartupParameters StartupParameters = {}; StartupParameters.hExeInstance = hInst; StartupParameters.iCmdShow = iCmdShow; diff --git a/Source/Application/Material.cpp b/Source/Application/Material.cpp new file mode 100644 index 00000000..ebc22cb9 --- /dev/null +++ b/Source/Application/Material.cpp @@ -0,0 +1,20 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#include "Material.h" +#include "Scene.h" diff --git a/Source/Application/Material.h b/Source/Application/Material.h new file mode 100644 index 00000000..0bbe54d0 --- /dev/null +++ b/Source/Application/Material.h @@ -0,0 +1,101 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#pragma once +#include + +#include "Types.h" + +enum EMaterialTextureMapBindings +{ + ALBEDO = 0, + NORMALS, + EMISSIVE, + // HEIGHT, + // SPECULAR, + ALPHA_MASK, + METALLIC, + ROUGHNESS, + + NUM_MATERIAL_TEXTURE_MAP_BINDINGS +}; + +struct Material // 56 Bytes +{ + //------------------------------------------------------------ + MaterialID ID = INVALID_ID; // 4 Bytes + DirectX::XMFLOAT3 diffuse = {1, 1, 1}; // 12 Bytes + //------------------------------------------------------------ + float alpha = 1.0f; // 4 Bytes + DirectX::XMFLOAT3 specular = { 1, 1, 1 }; // 12 Bytes + //------------------------------------------------------------ + DirectX::XMFLOAT3 emissiveColor = { 1, 1, 1 }; // 12 Bytes + float emissiveIntensity = 0.0f; // 4 Bytes + //------------------------------------------------------------ + DirectX::XMFLOAT2 tiling = { 1, 1 }; // 8 Bytes + DirectX::XMFLOAT2 uv_bias = { 0, 0 }; // 8 Bytes + //------------------------------------------------------------ + // Cook-Torrence BRDF + float metalness = 0.0f; // 4 Bytes + float roughness = 0.8f; // 4 Bytes + float pad0, pad1; // 8 Bytes + //------------------------------------------------------------ + + TextureID TexDiffuseMap = INVALID_ID; + TextureID TexNormalMap = INVALID_ID; + TextureID TexEmissiveMap = INVALID_ID; + TextureID TexHeightMap = INVALID_ID; + TextureID TexSpecularMap = INVALID_ID; // phong? + TextureID TexAlphaMaskMap = INVALID_ID; + TextureID TexMetallicMap = INVALID_ID; + TextureID TexRoughnessMap = INVALID_ID; + + SRV_ID SRVMaterialMaps = INVALID_ID; + //------------------------------------------------------------ + + inline bool IsTransparent() const { return alpha != 1.0f; } + +#if 0 + Material(MaterialID _ID); + ~Material(); + + void SetMaterialConstants(Renderer* renderer, EShaders shader, bool bIsDeferredRendering) const; + int GetTextureConfig() const; + inline bool HasTexture() const { return GetTextureConfig() != 0; } + + virtual SurfaceMaterial GetCBufferData() const = 0; + virtual void Clear() = 0; + + static SurfaceMaterial GetDefaultMaterialCBufferData(); +}; + +struct BRDF_Material : public Material +{ // Cook-Torrence BRDF + float metalness; + float roughness; + + BRDF_Material() : Material({ -1 }), metalness(0.0f), roughness(0.0f) {} + + SurfaceMaterial GetCBufferData() const override; + void Clear() override; + +private: + friend class MaterialPool; // only MaterialPool can create Material instances + BRDF_Material(MaterialID _ID) : Material(_ID), metalness(0.1f), roughness(0.6f) {} +#endif +}; \ No newline at end of file diff --git a/Source/Application/Math.h b/Source/Application/Math.h index 36c903b0..a7bbd9bf 100644 --- a/Source/Application/Math.h +++ b/Source/Application/Math.h @@ -20,10 +20,11 @@ #include + #define DEG2RAD (DirectX::XM_PI / 180.0f) #define RAD2DEG (180.0f / DirectX::XM_PI) -#define PI DirectX::XM_PI -#define PI_DIV2 DirectX::XM_PIDIV2 +constexpr float PI_DIV2 = DirectX::XM_PIDIV2; +constexpr float PI = DirectX::XM_PI; constexpr DirectX::XMFLOAT3 UpVector = DirectX::XMFLOAT3( 0, 1, 0); constexpr DirectX::XMFLOAT3 RightVector = DirectX::XMFLOAT3( 1, 0, 0); @@ -38,4 +39,70 @@ constexpr DirectX::XMFLOAT3 ZAxis = DirectX::XMFLOAT3(0, 0, 1); DirectX::XMFLOAT4X4 MakeOthographicProjectionMatrix(float screenWidth, float screenHeight, float screenNear, float screenFar); DirectX::XMFLOAT4X4 MakePerspectiveProjectionMatrix(float fovy, float screenAspect, float screenNear, float screenFar); -DirectX::XMFLOAT4X4 MakePerspectiveProjectionMatrixHFov(float fovx, float screenAspectInverse, float screenNear, float screenFar); \ No newline at end of file +DirectX::XMFLOAT4X4 MakePerspectiveProjectionMatrixHFov(float fovx, float screenAspectInverse, float screenNear, float screenFar); + +struct FFrustumPlaneset +{ // plane equations: aX + bY + cZ + d = 0 + DirectX::XMFLOAT4 abcd[6]; // r, l, t, b, n, f + enum EPlaneset + { + PL_RIGHT = 0, + PL_LEFT, + PL_TOP, + PL_BOTTOM, + PL_FAR, + PL_NEAR + }; + + // src: http://gamedevs.org/uploads/fast-extraction-viewing-frustum-planes-from-world-view-projection-matrix.pdfe + // gets the frustum planes based on @projectionTransformation. if: + // + // - @projectionTransformation is proj matrix -> view space plane equations + // - @projectionTransformation is viewProj matrix -> world space plane equations + // - @projectionTransformation is worldViewProj matrix -> model space plane equations + // + inline static FFrustumPlaneset ExtractFromMatrix(const DirectX::XMMATRIX& projectionTransformation) + { + const DirectX::XMMATRIX& m = projectionTransformation; + + FFrustumPlaneset viewPlanes; + // TODO: XMVECTOR impl; + viewPlanes.abcd[FFrustumPlaneset::PL_RIGHT] = DirectX::XMFLOAT4( + m.r[0].m128_f32[3] - m.r[0].m128_f32[0], + m.r[1].m128_f32[3] - m.r[1].m128_f32[0], + m.r[2].m128_f32[3] - m.r[2].m128_f32[0], + m.r[3].m128_f32[3] - m.r[3].m128_f32[0] + ); + viewPlanes.abcd[FFrustumPlaneset::PL_LEFT] = DirectX::XMFLOAT4( + m.r[0].m128_f32[3] + m.r[0].m128_f32[0], + m.r[1].m128_f32[3] + m.r[1].m128_f32[0], + m.r[2].m128_f32[3] + m.r[2].m128_f32[0], + m.r[3].m128_f32[3] + m.r[3].m128_f32[0] + ); + viewPlanes.abcd[FFrustumPlaneset::PL_TOP] = DirectX::XMFLOAT4( + m.r[0].m128_f32[3] - m.r[0].m128_f32[1], + m.r[1].m128_f32[3] - m.r[1].m128_f32[1], + m.r[2].m128_f32[3] - m.r[2].m128_f32[1], + m.r[3].m128_f32[3] - m.r[3].m128_f32[1] + ); + viewPlanes.abcd[FFrustumPlaneset::PL_BOTTOM] = DirectX::XMFLOAT4( + m.r[0].m128_f32[3] + m.r[0].m128_f32[1], + m.r[1].m128_f32[3] + m.r[1].m128_f32[1], + m.r[2].m128_f32[3] + m.r[2].m128_f32[1], + m.r[3].m128_f32[3] + m.r[3].m128_f32[1] + ); + viewPlanes.abcd[FFrustumPlaneset::PL_FAR] = DirectX::XMFLOAT4( + m.r[0].m128_f32[3] - m.r[0].m128_f32[2], + m.r[1].m128_f32[3] - m.r[1].m128_f32[2], + m.r[2].m128_f32[3] - m.r[2].m128_f32[2], + m.r[3].m128_f32[3] - m.r[3].m128_f32[2] + ); + viewPlanes.abcd[FFrustumPlaneset::PL_NEAR] = DirectX::XMFLOAT4( + m.r[0].m128_f32[2], + m.r[1].m128_f32[2], + m.r[2].m128_f32[2], + m.r[3].m128_f32[2] + ); + return viewPlanes; + } +}; diff --git a/Source/Application/Memory.cpp b/Source/Application/Memory.cpp new file mode 100644 index 00000000..7d3d1a20 --- /dev/null +++ b/Source/Application/Memory.cpp @@ -0,0 +1,21 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com +#pragma once + +#include "Memory.h" + diff --git a/Source/Application/Memory.h b/Source/Application/Memory.h new file mode 100644 index 00000000..eec399dd --- /dev/null +++ b/Source/Application/Memory.h @@ -0,0 +1,204 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com +#pragma once + +// +// Resources on memory management +// +// - https://akkadia.org/drepper/cpumemory.pdf +// - http://allenchou.net/memory-management-series/ +// - https://www.gamasutra.com/blogs/MichaelKissner/20151104/258271/Writing_a_Game_Engine_from_Scratch__Part_2_Memory.php +// - https://gamasutra.com/blogs/MichaelKissner/20151120/259561/Writing_a_Game_Engine_from_Scratch__Part_3_Data__Cache.php +// - http://dmitrysoshnikov.com/compilers/writing-a-memory-allocator/ +// - http://dmitrysoshnikov.com/compilers/writing-a-pool-allocator/ +// - https://blog.molecular-matters.com/2012/09/17/memory-allocation-strategies-a-pool-allocator/ +// + +// http://dmitrysoshnikov.com/compilers/writing-a-memory-allocator/ +inline constexpr size_t AlignTo(size_t size, size_t alignment = 64) +{ + return (size + alignment - 1) & ~(alignment - 1); +} + +#define MEMORY_POOL__ENABLE_DEBUG_LOG 0 +#define MEMORY_POOL__LOG_VERBOSE 0 +#if MEMORY_POOL__ENABLE_DEBUG_LOG +#include "../../Libs/VQUtils/Source/Log.h" +#include "../../Libs/VQUtils/Source/utils.h" +#endif + + +// +// MEMORY POOL +// +template +class MemoryPool +{ +public: + MemoryPool(size_t NumBlocks, size_t Alignment); + ~MemoryPool(); + + TObject* Allocate(size_t NumBlocks = 1); + void Free(void* pBlock); + + +#if MEMORY_POOL__ENABLE_DEBUG_LOG + void PrintDebugInfo() const; +#endif +private: + struct Block { Block* pNext; }; + + // memory + Block* mpNextFreeBlock = nullptr; + void* mpAlloc = nullptr; + + // header + size_t mNumBlocks = 0; + size_t mNumUsedBlocks = 0; + size_t mAllocSize = 0; +}; + + + + +// +// MemoryPool Template Implementation +// +template +inline MemoryPool::MemoryPool(size_t NumBlocks, size_t Alignment) + : mNumBlocks(NumBlocks) +{ + // calc alloc size + const size_t AlignedObjSize = AlignTo(sizeof(TObject), Alignment); + const size_t AllocSize = AlignedObjSize * NumBlocks; + + // alloc mem + this->mpNextFreeBlock = reinterpret_cast(malloc(AllocSize)); +#if MEMORY_POOL__ENABLE_DEBUG_LOG + if (!this->mpNextFreeBlock) Log::Error("MemoryPool(NumBlocks=%d, Alignment=%d): malloc() failed", NumBlocks, Alignment); +#endif + assert(this->mpNextFreeBlock); + this->mpAlloc = this->mpNextFreeBlock; + this->mAllocSize = AllocSize; + + // setup list structure + Block* pWalk = this->mpNextFreeBlock; + Block* pNextBlock = (Block*)(((unsigned char*)this->mpNextFreeBlock) + AlignedObjSize); + for (size_t i = 1; i < NumBlocks; ++i) + { + if (i == NumBlocks - 1) + { + pWalk->pNext = nullptr; + break; + } + pWalk->pNext = pNextBlock; + pWalk = pNextBlock; + pNextBlock = (Block*)((unsigned char*)(pNextBlock) + AlignedObjSize); + } + + +#if MEMORY_POOL__ENABLE_DEBUG_LOG + Log::Info("MemoryPool: Created pool w/ ObjectSize=%s, Alignment=%s, AlignedObjectSize=%s, NumBlocks=%d, AllocSize=%s, mpAlloc=0x%x" + , StrUtil::FormatByte(sizeof(TObject)).c_str() + , StrUtil::FormatByte(Alignment).c_str() + , StrUtil::FormatByte(AlignedObjSize).c_str() + , NumBlocks + , StrUtil::FormatByte(AllocSize).c_str() + , mpAlloc + ); + PrintDebugInfo(); +#endif +} + +template +inline MemoryPool::~MemoryPool() +{ + if (mNumUsedBlocks != 0) + { + Log::Warning("~MemoryPool() : mNumUsedBlocks != 0, did you Free() all allocated objects from the Scene?"); + + // if you hit this, Scene has 'leaked' memory. + // The application will still deallocate the memory and won't really leak, + // but the pointers that weren't freed will be dangling. + ///assert(mNumUsedBlocks == 0); + } + + if (mpAlloc) + free(mpAlloc); +} + +template +inline TObject* MemoryPool::Allocate(size_t NumBlocks) +{ + // TODO: alloc NumBlocks? + //for (size_t i = 0; i < NumBlocks; ++i) + //{ + // ; + //} + + if (!mpNextFreeBlock) + { + assert(this->mNumUsedBlocks == this->mNumBlocks); + Log::Error("MemoryPool is out of memory"); + assert(mpNextFreeBlock); // if you hit this, allocate a bigger pool on startup + } + + ++this->mNumUsedBlocks; + + // only alloc 1 for now + TObject* pNewObj = (TObject*)mpNextFreeBlock; + mpNextFreeBlock = mpNextFreeBlock->pNext; + return pNewObj; +} + +template +inline void MemoryPool::Free(void* pBlock) +{ + assert(pBlock); + Block* pMem = (Block*)pBlock; + pMem->pNext = this->mpNextFreeBlock; + this->mpNextFreeBlock = pMem; + --this->mNumUsedBlocks; +} + + +#if MEMORY_POOL__ENABLE_DEBUG_LOG +template +inline void MemoryPool::PrintDebugInfo() const +{ + Log::Info("-----------------"); + Log::Info("Memory Pool"); + Log::Info("Allocation Size : %s", StrUtil::FormatByte(this->mAllocSize).c_str()); + Log::Info("Total # Blocks : %d", this->mNumBlocks); + Log::Info("Used # Blocks : %d", this->mNumUsedBlocks); + Log::Info("Next Available : 0x%x %s", this->mpNextFreeBlock, (this->mpNextFreeBlock == this->mpAlloc ? "(HEAD)" : "")); + Log::Info("-----------------"); +#if MEMORY_POOL__LOG_VERBOSE + size_t iBlock = 0; + Block* pWalk = mpNextFreeBlock; + while (pWalk) + { + Log::Info("[%d] 0x%x", iBlock, pWalk); + + ++iBlock; + pWalk = pWalk->pNext; + } + Log::Info("-----------------"); +#endif +} +#endif \ No newline at end of file diff --git a/Source/Application/Mesh.cpp b/Source/Application/Mesh.cpp index 8977cdd3..3bdbcf84 100644 --- a/Source/Application/Mesh.cpp +++ b/Source/Application/Mesh.cpp @@ -1,5 +1,5 @@ -// VQEngine | DirectX11 Renderer -// Copyright(C) 2018 - Volkan Ilbeyli +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli // // This program is free software : you can redistribute it and / or modify // it under the terms of the GNU General Public License as published by @@ -25,6 +25,17 @@ #endif +EBuiltInMeshes Mesh::GetBuiltInMeshType(const std::string& MeshTypeStr) +{ + static std::unordered_map MESH_TYPE_LOOKUP = + { + { "Cube", EBuiltInMeshes::CUBE } + , { "Triangle", EBuiltInMeshes::TRIANGLE } + // TODO + }; + return MESH_TYPE_LOOKUP.at(MeshTypeStr); +} + std::pair Mesh::GetIABufferIDs(int lod /*= 0*/) const { assert(mLODBufferPairs.size() > 0); // maybe no assert and return <-1, -1> ? diff --git a/Source/Application/Mesh.h b/Source/Application/Mesh.h index 93a8be54..83a3abaa 100644 --- a/Source/Application/Mesh.h +++ b/Source/Application/Mesh.h @@ -1,5 +1,5 @@ -// VQEngine | DirectX11 Renderer -// Copyright(C) 2018 - Volkan Ilbeyli +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli // // This program is free software : you can redistribute it and / or modify // it under the terms of the GNU General Public License as published by @@ -35,6 +35,7 @@ enum EBuiltInMeshes NUM_BUILTIN_MESHES }; + struct VertexIndexBufferIDPair @@ -64,6 +65,7 @@ struct MeshLODData struct Mesh { public: + static EBuiltInMeshes GetBuiltInMeshType(const std::string& MeshTypeStr); // // Constructors / Operators // @@ -76,7 +78,7 @@ struct Mesh ); template - Mesh(const MeshLODData& meshLODData); + Mesh(VQRenderer* pRenderer, const MeshLODData& meshLODData); Mesh() = default; // Mesh() = delete; @@ -109,6 +111,7 @@ Mesh::Mesh( const std::string& name ) { + assert(pRenderer); FBufferDesc bufferDesc = {}; const std::string VBName = name + "_LOD[0]_VB"; @@ -134,7 +137,7 @@ Mesh::Mesh( } template -Mesh::Mesh(const MeshLODData& meshLODData) +Mesh::Mesh(VQRenderer* pRenderer, const MeshLODData& meshLODData) { for (size_t LOD = 0; LOD < meshLODData.LODVertices.size(); ++LOD) { @@ -147,15 +150,19 @@ Mesh::Mesh(const MeshLODData& meshLODData) //bufferDesc.mUsage = GPU_READ_WRITE; bufferDesc.mElementCount = static_cast(meshLODData.LODVertices[LOD].size()); bufferDesc.mStride = sizeof(meshLODData.LODVertices[LOD][0]); - BufferID vertexBufferID = spRenderer->CreateBuffer(bufferDesc, meshLODData.LODVertices[LOD].data(), VBName.c_str()); + BufferID vertexBufferID = pRenderer->CreateBuffer(bufferDesc, meshLODData.LODVertices[LOD].data(), VBName.c_str()); bufferDesc.mType = INDEX_BUFFER; //bufferDesc.mUsage = GPU_READ_WRITE; bufferDesc.NumElements = static_cast(meshLODData.LODIndices[LOD].size()); bufferDesc.mStride = sizeof(unsigned); - BufferID indexBufferID = spRenderer->CreateBuffer(bufferDesc, meshLODData.LODIndices[LOD].data(), IBName.c_str()); + BufferID indexBufferID = pRenderer->CreateBuffer(bufferDesc, meshLODData.LODIndices[LOD].data(), IBName.c_str()); mLODBufferPairs.push_back({ vertexBufferID, indexBufferID }); mNumIndicesPerLODLevel.push_back(bufferDesc.NumElements); } } + + + +using BuiltinMeshArray_t = std::array; \ No newline at end of file diff --git a/Source/Application/Model.cpp b/Source/Application/Model.cpp new file mode 100644 index 00000000..ac6a3704 --- /dev/null +++ b/Source/Application/Model.cpp @@ -0,0 +1,27 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#include "Model.h" + +#include + +bool Model::Data::AddMaterial(MeshID meshID, MaterialID matID, bool bTransparent) +{ + assert(false); + return false; +} diff --git a/Source/Application/Model.h b/Source/Application/Model.h new file mode 100644 index 00000000..97852bfc --- /dev/null +++ b/Source/Application/Model.h @@ -0,0 +1,79 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com +#pragma once + +#include "Types.h" + +#include +#include + +class VQRenderer; + +struct MeshRenderSettings +{ + enum EMeshRenderMode + { + FILL = 0, + WIREFRAME, + + NUM_MESH_RENDER_MODES + }; + static EMeshRenderMode DefaultRenderSettings() { return { EMeshRenderMode::FILL }; } + EMeshRenderMode renderMode = EMeshRenderMode::FILL; +}; + +using MeshMaterialLookup_t = std::unordered_map; +using MeshRenderSettingsLookup_t = std::unordered_map; + + +// +// MODEL +// +struct Model +{ +public: + struct Data + { + std::vector mOpaueMeshIDs; + std::vector mTransparentMeshIDs; + MeshMaterialLookup_t mOpaqueMaterials; + MeshMaterialLookup_t mTransparentMaterials; + inline bool HasMaterial() const { return !mOpaqueMaterials.empty() || !mTransparentMaterials.empty(); } + bool AddMaterial(MeshID meshID, MaterialID matID, bool bTransparent = false); + }; + + //--------------------------------------- + + Model() = default; + Model(const std::string& directoryFullPath, const std::string& modelName, Data&& modelDataIn) + : mData(modelDataIn) + , mModelName(modelName) + , mModelPath(directoryFullPath) + , mbLoaded(true) + {} + + //--------------------------------------- + + Data mData; + std::string mModelName; + std::string mModelPath; + bool mbLoaded = false; +}; + + + diff --git a/Source/Application/Platform.h b/Source/Application/Platform.h index 341dd716..5711753f 100644 --- a/Source/Application/Platform.h +++ b/Source/Application/Platform.h @@ -57,10 +57,13 @@ struct FStartupParameters uint8 bOverrideENGSetting_bAutomatedTest : 1; uint8 bOverrideENGSetting_bTestFrames : 1; - - std::string LevelNameToLoad = "DefaultScene"; + uint8 bOverrideENGSetting_StartupScene : 1; }; LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // ------------------------------------------------------------------------------- + + +template static inline T CircularIncrement(T currVal, T maxVal) { return (currVal + 1) % maxVal; } +template static inline T CircularDecrement(T currVal, T maxVal, T minVal = 0) { return currVal == minVal ? maxVal : currVal - 1; } \ No newline at end of file diff --git a/Source/Application/Quaternion.h b/Source/Application/Quaternion.h index 087aec0f..23676ece 100644 --- a/Source/Application/Quaternion.h +++ b/Source/Application/Quaternion.h @@ -33,6 +33,7 @@ class Quaternion Quaternion(const DirectX::XMMATRIX& rotMatrix); Quaternion(float s, const DirectX::XMVECTOR& v); + Quaternion(float s, const DirectX::XMFLOAT3& v) : S(s), V(v) {}; Quaternion operator+(const Quaternion& q) const; Quaternion operator*(const Quaternion& q) const; @@ -48,7 +49,6 @@ class Quaternion DirectX::XMFLOAT3 TransformVector(const DirectX::XMFLOAT3& v) const; DirectX::XMVECTOR TransformVector(const DirectX::XMVECTOR& v) const; private: // used by operator()s - Quaternion(float s, const DirectX::XMFLOAT3& v) : S(s), V(v) {}; Quaternion() = default; public: diff --git a/Source/Application/Scene.cpp b/Source/Application/Scene.cpp new file mode 100644 index 00000000..ed15e930 --- /dev/null +++ b/Source/Application/Scene.cpp @@ -0,0 +1,460 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com + +#define NOMINMAX + +#include "Scene.h" +#include "Window.h" +#include "VQEngine.h" + +#include "Libs/VQUtils/Source/utils.h" + +#define LOG_CACHED_RESOURCES_ON_LOAD 0 +#define LOG_RESOURCE_CREATE 1 + +using namespace DirectX; + +static MeshID LAST_USED_MESH_ID = EBuiltInMeshes::NUM_BUILTIN_MESHES; + +//MeshID Scene::CreateMesh() +//{ +// ModelID id = LAST_USED_MESH_ID++; +// +// mMeshes[id] = Mesh(); +// return id; +//} + +MeshID Scene::AddMesh(Mesh&& mesh) +{ + std::lock_guard lk(mMtx_Meshes); + MeshID id = LAST_USED_MESH_ID++; + mMeshes[id] = mesh; + return id; +} + +MeshID Scene::AddMesh(const Mesh& mesh) +{ + std::lock_guard lk(mMtx_Meshes); + MeshID id = LAST_USED_MESH_ID++; + mMeshes[id] = mesh; + return id; +} + +ModelID Scene::CreateModel() +{ + std::unique_lock lk(mMtx_Models); + static ModelID LAST_USED_MODEL_ID = 0; + ModelID id = LAST_USED_MODEL_ID++; + mModels[id] = Model(); + return id; +} + +MaterialID Scene::CreateMaterial(const std::string& UniqueMaterialName) +{ + auto it = mLoadedMaterials.find(UniqueMaterialName); + if (it != mLoadedMaterials.end()) + { +#if LOG_CACHED_RESOURCES_ON_LOAD + Log::Info("Material already loaded: %s", UniqueMaterialName.c_str()); +#endif + return it->second; + } + + static MaterialID LAST_USED_MATERIAL_ID = 0; + MaterialID id = INVALID_ID; + // critical section + { + std::unique_lock lk(mMtx_Materials); + id = LAST_USED_MATERIAL_ID++; + } + mMaterials[id] = Material(); + mLoadedMaterials[UniqueMaterialName] = id; +#if LOG_RESOURCE_CREATE + Log::Info("Scene::CreateMaterial() ID=%d - %s", id, UniqueMaterialName.c_str()); +#endif + + Material& mat = mMaterials.at(id); + if (mat.SRVMaterialMaps == INVALID_ID) + { + mat.SRVMaterialMaps = mRenderer.CreateSRV(NUM_MATERIAL_TEXTURE_MAP_BINDINGS); + mRenderer.InitializeSRV(mat.SRVMaterialMaps, 0, INVALID_ID); + mRenderer.InitializeSRV(mat.SRVMaterialMaps, 1, INVALID_ID); + mRenderer.InitializeSRV(mat.SRVMaterialMaps, 2, INVALID_ID); + mRenderer.InitializeSRV(mat.SRVMaterialMaps, 3, INVALID_ID); + mRenderer.InitializeSRV(mat.SRVMaterialMaps, 4, INVALID_ID); + mRenderer.InitializeSRV(mat.SRVMaterialMaps, 5, INVALID_ID); + } + return id; +} + +Material& Scene::GetMaterial(MaterialID ID) +{ + if (mMaterials.find(ID) == mMaterials.end()) + { + Log::Error("Material not created. Did you call Scene::CreateMaterial()? (matID=%d)", ID); + assert(false); + } + return mMaterials.at(ID); +} + +Model& Scene::GetModel(ModelID id) +{ + if (mModels.find(id) == mModels.end()) + { + Log::Error("Model not created. Did you call Scene::CreateModel()? (modelID=%d)", id); + assert(false); + } + return mModels.at(id); +} + + +Scene::Scene(VQEngine& engine, int NumFrameBuffers, const Input& input, const std::unique_ptr& pWin, VQRenderer& renderer) + : mInput(input) + , mpWindow(pWin) + , mEngine(engine) + , mFrameSceneViews(NumFrameBuffers) + , mIndex_SelectedCamera(0) + , mIndex_ActiveEnvironmentMapPreset(0) + , mGameObjectPool(NUM_GAMEOBJECT_POOL_SIZE, GAMEOBJECT_BYTE_ALIGNMENT) + , mTransformPool(NUM_GAMEOBJECT_POOL_SIZE, GAMEOBJECT_BYTE_ALIGNMENT) + , mResourceNames(engine.GetResourceNames()) + , mAssetLoader(engine.GetAssetLoader()) + , mRenderer(renderer) + , mMaterialAssignments(engine.GetAssetLoader().GetThreadPool_TextureLoad()) +{} + +void Scene::Update(float dt, int FRAME_DATA_INDEX) +{ + assert(FRAME_DATA_INDEX < mFrameSceneViews.size()); + FSceneView& SceneView = mFrameSceneViews[FRAME_DATA_INDEX]; + Camera& Cam = this->mCameras[this->mIndex_SelectedCamera]; + + Cam.Update(dt, mInput); + this->HandleInput(); + this->UpdateScene(dt, SceneView); +} + +void Scene::PostUpdate(int FRAME_DATA_INDEX, int FRAME_DATA_NEXT_INDEX) +{ + assert(FRAME_DATA_INDEX < mFrameSceneViews.size()); + FSceneView& SceneView = mFrameSceneViews[FRAME_DATA_INDEX]; + FSceneView& SceneViewNext = mFrameSceneViews[FRAME_DATA_NEXT_INDEX]; + + const Camera& cam = mCameras[mIndex_SelectedCamera]; + const XMFLOAT3 camPos = cam.GetPositionF(); + + // extract scene view + SceneView.proj = cam.GetProjectionMatrix(); + SceneView.projInverse = XMMatrixInverse(NULL, SceneView.proj); + SceneView.view = cam.GetViewMatrix(); + SceneView.viewInverse = cam.GetViewInverseMatrix(); + SceneView.viewProj = SceneView.view * SceneView.proj; + SceneView.cameraPosition = XMLoadFloat3(&camPos); + SceneView.MainViewCameraYaw = cam.GetYaw(); + SceneView.MainViewCameraPitch = cam.GetPitch(); + + // TODO: compute visibility + + SceneView.meshRenderCommands.clear(); + for (const GameObject* pObj : mpObjects) + { + const XMMATRIX matWorldTransform = mpTransforms.at(pObj->mTransformID)->WorldTransformationMatrix(); + + const bool bModelNotFound = mModels.find(pObj->mModelID) == mModels.end(); + if (bModelNotFound) + { + Log::Warning("[Scene] Model not found: ID=%d", pObj->mModelID); + continue; + } + + const Model& model = mModels.at(pObj->mModelID); + + assert(pObj->mModelID != INVALID_ID); + for (const MeshID id : model.mData.mOpaueMeshIDs) + { + FMeshRenderCommand meshRenderCmd; + meshRenderCmd.meshID = id; + meshRenderCmd.WorldTransformationMatrix = matWorldTransform; + meshRenderCmd.matID = model.mData.mOpaqueMaterials.at(id); + + SceneView.meshRenderCommands.push_back(meshRenderCmd); + } + } + + // update post process settings for next frame + SceneViewNext.postProcess = SceneView.postProcess; +} + +void Scene::StartLoading(const BuiltinMeshArray_t& builtinMeshes, FSceneRepresentation& scene) +{ + mRenderer.WaitForLoadCompletion(); + + Log::Info("[Scene] Loading Scene: %s", scene.SceneName.c_str()); + + constexpr bool B_LOAD_SERIAL = true; + auto fnDeserializeGameObject = [&](FGameObjectRepresentation& ObjRep) + { + // GameObject + GameObject* pObj = mGameObjectPool.Allocate(1); + pObj->mModelID = INVALID_ID; + pObj->mTransformID = INVALID_ID; + + // Transform + Transform* pTransform = mTransformPool.Allocate(1); + *pTransform = std::move(ObjRep.tf); + mpTransforms.push_back(pTransform); + + TransformID tID = static_cast(mpTransforms.size() - 1); + pObj->mTransformID = tID; + + // Model + const bool bModelIsBuiltinMesh = !ObjRep.BuiltinMeshName.empty(); + const bool bModelIsLoadedFromFile = !ObjRep.ModelFilePath.empty(); + assert(bModelIsBuiltinMesh != bModelIsLoadedFromFile); + + if (bModelIsBuiltinMesh) + { + ModelID mID = this->CreateModel(); + Model& model = mModels.at(mID); + + // create/get mesh + MeshID meshID = mEngine.GetBuiltInMeshID(ObjRep.BuiltinMeshName); + model.mData.mOpaueMeshIDs.push_back(meshID); + + // material + MaterialID matID = this->mDefaultMaterialID; + if (!ObjRep.MaterialName.empty()) + { + matID = this->CreateMaterial(ObjRep.MaterialName); + } + Material& mat = this->GetMaterial(matID); + const bool bTransparentMesh = mat.IsTransparent(); + model.mData.mOpaqueMaterials[meshID] = matID; // todo: handle transparency + + model.mbLoaded = true; + pObj->mModelID = mID; + } + else + { + mAssetLoader.QueueModelLoad(pObj, ObjRep.ModelFilePath, ObjRep.ModelName); + } + + + mpObjects.push_back(pObj); + }; + + // register builtin meshes to scene mesh lookup + // @mMeshes[0-NUM_BUILTIN_MESHES] are assigned here directly while the rest + // of the meshes used in the scene must use this->AddMesh(Mesh&&) interface; + for (size_t i = 0; i < builtinMeshes.size(); ++i) + { + this->mMeshes[(int)i] = builtinMeshes[i]; + } + + // register builtin materials + { + this->mDefaultMaterialID = this->CreateMaterial("DefaultMaterial"); + Material& mat = this->GetMaterial(this->mDefaultMaterialID); + } + + // scene-specific load + this->LoadScene(scene); + + AssetLoader::LoadTaskID taskID = AssetLoader::GenerateLoadTaskID(); + + // Create scene materials before deserializing gameobjects + for (const FMaterialRepresentation& matRep : scene.Materials) + { + MaterialID id = this->CreateMaterial(matRep.Name); + Material& mat = this->GetMaterial(id); + + auto fnAssignF = [](float& dest, const float& src) { if (src != MATERIAL_UNINITIALIZED_VALUE) dest = src; }; + auto fnAssignF3 = [](XMFLOAT3& dest, const XMFLOAT3& src) { if (src.x != MATERIAL_UNINITIALIZED_VALUE) dest = src; }; + auto fnEnqueueTexLoad = [&](MaterialID matID, const std::string& path, AssetLoader::ETextureType type) + { + if (path.empty()) return; + AssetLoader::FTextureLoadParams p = {}; + p.MatID = matID; + p.TexturePath = path; + p.TexType = type; + mAssetLoader.QueueTextureLoad(taskID, p); + }; + + // immediate values + fnAssignF(mat.alpha, matRep.Alpha); + fnAssignF(mat.metalness, matRep.Metalness); + fnAssignF(mat.roughness, matRep.Roughness); + fnAssignF(mat.emissiveIntensity, matRep.EmissiveIntensity); + fnAssignF3(mat.emissiveColor, matRep.EmissiveColor); + fnAssignF3(mat.diffuse, matRep.DiffuseColor); + + // async data (textures) + fnEnqueueTexLoad(id, matRep.DiffuseMapFilePath , AssetLoader::ETextureType::DIFFUSE); + fnEnqueueTexLoad(id, matRep.NormalMapFilePath , AssetLoader::ETextureType::NORMALS); + fnEnqueueTexLoad(id, matRep.EmissiveMapFilePath , AssetLoader::ETextureType::EMISSIVE); + fnEnqueueTexLoad(id, matRep.AlphaMaskMapFilePath, AssetLoader::ETextureType::ALPHA_MASK); + fnEnqueueTexLoad(id, matRep.MetallicMapFilePath , AssetLoader::ETextureType::METALNESS); + fnEnqueueTexLoad(id, matRep.RoughnessMapFilePath, AssetLoader::ETextureType::ROUGHNESS); + + AssetLoader::FMaterialTextureAssignment MatTexAssignment = {}; + MatTexAssignment.matID = id; + mMaterialAssignments.mAssignments.push_back(MatTexAssignment); + } + Log::Info("[Scene] Materials Created"); + + if(!mMaterialAssignments.mAssignments.empty()) + mMaterialAssignments.mTextureLoadResults = mAssetLoader.StartLoadingTextures(taskID); + + // start loading material textures + Log::Info("[Scene] Start loading textures..."); + + if constexpr (B_LOAD_SERIAL) + { + // GAME OBJECTS + for (FGameObjectRepresentation& ObjRep : scene.Objects) + { + fnDeserializeGameObject(ObjRep); + } + } + else // THREADED LOAD + { + // dispatch workers + assert(false); // TODO + } + + mModelLoadResults = mAssetLoader.StartLoadingModels(this); + Log::Info("[Scene] Start loading models..."); + + // CAMERAS + for (FCameraParameters& param : scene.Cameras) + { + param.ProjectionParams.ViewportWidth = static_cast( mpWindow->GetWidth() ); + param.ProjectionParams.ViewportHeight = static_cast( mpWindow->GetHeight() ); + + Camera c; + c.InitializeCamera(param); + mCameras.emplace_back(std::move(c)); + } + + // CAMERA CONTROLLERS + // controllers need to be initialized after mCameras are populated in order + // to prevent dangling pointers in @pController->mpCamera (circular ptrs) + for (size_t i = 0; i < mCameras.size(); ++i) + { + if (scene.Cameras[i].bInitializeCameraController) + { + mCameras[i].InitializeController(scene.Cameras[i].bFirstPerson, scene.Cameras[i]); + } + } + Log::Info("[Scene] Cameras initialized"); + + + // assign scene rep + mSceneRepresentation = scene; +} + +void Scene::OnLoadComplete() +{ + Log::Info("[Scene] OnLoadComplete()"); + + // Assign model data + for (auto it = mModelLoadResults.begin(); it != mModelLoadResults.end(); ++it) + { + GameObject* pObj = it->first; + AssetLoader::ModelLoadResult_t res = std::move(it->second); + + assert(res.valid()); + ///res.wait(); // we should already have the results ready in OnLoadComplete() + + pObj->mModelID = res.get(); + } + + mMaterialAssignments.DoAssignments(this, &mRenderer); + + Log::Info("[Scene] %s loaded.", mSceneRepresentation.SceneName.c_str()); + mSceneRepresentation.loadSuccess = 1; + this->InitializeScene(); +} + +void Scene::Unload() +{ + this->UnloadScene(); + + mSceneRepresentation = {}; + + const size_t sz = mFrameSceneViews.size(); + mFrameSceneViews.clear(); + mFrameSceneViews.resize(sz); + + //mMeshes.clear(); // TODO + + for (Transform* pTf : mpTransforms) mTransformPool.Free(pTf); + mpTransforms.clear(); + + for (GameObject* pObj : mpObjects) mGameObjectPool.Free(pObj); + mpObjects.clear(); + + mCameras.clear(); + + mDirectionalLight = {}; + mLightsStatic.clear(); + mLightsDynamic.clear(); + + mSceneBoundingBox = {}; + mMeshBoundingBoxes.clear(); + mGameObjectBoundingBoxes.clear(); + + mIndex_SelectedCamera = 0; + mIndex_ActiveEnvironmentMapPreset = -1; + mEngine.UnloadEnvironmentMap(); +} + +void Scene::RenderUI() +{ + // TODO +} + +void Scene::HandleInput() +{ + const bool bIsShiftDown = mInput.IsKeyDown("Shift"); + const int NumEnvMaps = static_cast(mResourceNames.mEnvironmentMapPresetNames.size()); + + if (mInput.IsKeyTriggered("C")) + { + const int NumCameras = static_cast(mCameras.size()); + mIndex_SelectedCamera = bIsShiftDown + ? CircularDecrement(mIndex_SelectedCamera, NumCameras) + : CircularIncrement(mIndex_SelectedCamera, NumCameras); + } + + if (mInput.IsKeyTriggered("PageUp")) + { + mIndex_ActiveEnvironmentMapPreset = CircularIncrement(mIndex_ActiveEnvironmentMapPreset, NumEnvMaps); + mEngine.StartLoadingEnvironmentMap(mIndex_ActiveEnvironmentMapPreset); + } + if (mInput.IsKeyTriggered("PageDown")) + { + mIndex_ActiveEnvironmentMapPreset = CircularDecrement(mIndex_ActiveEnvironmentMapPreset, NumEnvMaps - 1); + mEngine.StartLoadingEnvironmentMap(mIndex_ActiveEnvironmentMapPreset); + } +} + +FMaterialRepresentation::FMaterialRepresentation() + : DiffuseColor(MATERIAL_UNINITIALIZED_VALUE, MATERIAL_UNINITIALIZED_VALUE, MATERIAL_UNINITIALIZED_VALUE) + , EmissiveColor(MATERIAL_UNINITIALIZED_VALUE, MATERIAL_UNINITIALIZED_VALUE, MATERIAL_UNINITIALIZED_VALUE) +{} diff --git a/Source/Application/Scene.h b/Source/Application/Scene.h new file mode 100644 index 00000000..38aebd2d --- /dev/null +++ b/Source/Application/Scene.h @@ -0,0 +1,286 @@ +// VQE +// Copyright(C) 2020 - Volkan Ilbeyli +// +// This program is free software : you can redistribute it and / or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.If not, see . +// +// Contact: volkanilbeyli@gmail.com +#pragma once + +#include "Camera.h" +#include "Mesh.h" +#include "Material.h" +#include "Model.h" +#include "Light.h" +#include "Transform.h" +#include "GameObject.h" +#include "Memory.h" +#include "AssetLoader.h" + +class Input; +struct Material; +struct FResourceNames; + +//------------------------------------------------------ +#define MATERIAL_UNINITIALIZED_VALUE -1.0f +struct FMaterialRepresentation +{ + std::string Name; + DirectX::XMFLOAT3 DiffuseColor; + float Alpha = MATERIAL_UNINITIALIZED_VALUE; + DirectX::XMFLOAT3 EmissiveColor; + float EmissiveIntensity = MATERIAL_UNINITIALIZED_VALUE; + float Metalness = MATERIAL_UNINITIALIZED_VALUE; + float Roughness = MATERIAL_UNINITIALIZED_VALUE; + std::string DiffuseMapFilePath ; + std::string NormalMapFilePath ; + std::string EmissiveMapFilePath ; + std::string AlphaMaskMapFilePath; + std::string MetallicMapFilePath ; + std::string RoughnessMapFilePath; + + FMaterialRepresentation(); +}; +struct FGameObjectRepresentation +{ + Transform tf; + + std::string ModelName; + std::string ModelFilePath; + + std::string BuiltinMeshName; + std::string MaterialName; +}; +struct FSceneRepresentation +{ + std::string SceneName; + std::string EnvironmentMapPreset; + + std::vector Materials; + std::vector Cameras; + std::vector Objects; + //std::vector Lights; + + char loadSuccess = 0; +}; +//------------------------------------------------------ +struct FPostProcessParameters +{ + EColorSpace ContentColorSpace = EColorSpace::REC_709; + EDisplayCurve OutputDisplayCurve = EDisplayCurve::sRGB; + float DisplayReferenceBrightnessLevel = 200.0f; + int ToggleGammaCorrection = 1; +}; +struct FMeshRenderCommand +{ + MeshID meshID = INVALID_ID; + MaterialID matID = INVALID_ID; + DirectX::XMMATRIX WorldTransformationMatrix; // WorldTF ID ? +}; +struct FSceneView +{ + DirectX::XMMATRIX view; + DirectX::XMMATRIX viewProj; + DirectX::XMMATRIX viewInverse; + DirectX::XMMATRIX proj; + DirectX::XMMATRIX projInverse; + DirectX::XMMATRIX directionalLightProjection; + DirectX::XMVECTOR cameraPosition; + float MainViewCameraYaw = 0.0f; + float MainViewCameraPitch = 0.0f; + //bool bIsPBRLightingUsed; + //bool bIsDeferredRendering; + //bool bIsIBLEnabled; + //Settings::SceneRender sceneRenderSettings; + //EnvironmentMap environmentMap; + + FPostProcessParameters postProcess; + + std::vector meshRenderCommands; +}; +//------------------------------------------------------ + +constexpr size_t NUM_GAMEOBJECT_POOL_SIZE = 4096; +constexpr size_t GAMEOBJECT_BYTE_ALIGNMENT = 64; // assumed typical cache-line size + +//---------------------------------------------------------------------------------------------------------------- +// https://en.wikipedia.org/wiki/Template_method_pattern +// https://stackoverflow.com/questions/9724371/force-calling-base-class-virtual-function +// https://isocpp.org/wiki/faq/strange-inheritance#two-strategies-for-virtuals +// template method seems like a good idea here: +// The base class takes care of the common tasks among all scenes and calls the +// customized functions of the derived classes through pure virtual functions +// In other words, the particular scene implementations will have to override those functions +// so that each scene can have custom logic as desired. Similar to how you override those functions in Unity3D/C#. +//---------------------------------------------------------------------------------------------------------------- +class Scene +{ + // Scene class contains the scene data and the logic to manipulate it. + // Scene is essentially a small part of the Engine. Writing an entire interface + // for Scene to query scene data would be a waste of time without added benefit. + // Hence VQEngine is declared a friend and has easy acess to all data to + // effectively orchestrate communication between its multiple threads. + friend class VQEngine; + +//---------------------------------------------------------------------------------------------------------------- +// SCENE INTERFACE +//---------------------------------------------------------------------------------------------------------------- +protected: + // Scene-specific loading logic goes here. + // LoadScene() is called right before loading begins. + // + virtual void LoadScene(FSceneRepresentation& scene) = 0; + + // InitializeScene() is called after the scene data is loaded from the disk. + // + virtual void InitializeScene() = 0; + + // Update() is called each frame before Engine::Render(). Scene-specific update logic goes here. + // + virtual void UpdateScene(float dt, FSceneView& view) = 0; + + // Scene-specific unloading logic goes here + // + virtual void UnloadScene() = 0; + + // Each scene has to implement scene-specific RenderUI() function. + // RenderUI() is called after post processing is finished and it is + // the last rendering workload before presenting the frame. + // + virtual void RenderSceneUI() const = 0; + +//---------------------------------------------------------------------------------------------------------------- +// ENGINE INTERFACE +//---------------------------------------------------------------------------------------------------------------- +public: + Scene(VQEngine& engine + , int NumFrameBuffers + , const Input& input + , const std::unique_ptr& pWin + , VQRenderer& renderer + ); + +private: // Derived Scenes shouldn't access these functions + void Update(float dt, int FRAME_DATA_INDEX); + void PostUpdate(int FRAME_DATA_INDEX, int FRAME_DATA_NEXT_INDEX); + void StartLoading(const BuiltinMeshArray_t& builtinMeshes, FSceneRepresentation& scene); + void OnLoadComplete(); + void Unload(); // serial-only for now. maybe MT later. + void RenderUI(); + void HandleInput(); + +public: + inline const FSceneView& GetSceneView(int FRAME_DATA_INDEX) const { return mFrameSceneViews[FRAME_DATA_INDEX]; } + inline FPostProcessParameters& GetPostProcessParameters(int FRAME_DATA_INDEX) { return mFrameSceneViews[FRAME_DATA_INDEX].postProcess; } + inline const FPostProcessParameters& GetPostProcessParameters(int FRAME_DATA_INDEX) const { return mFrameSceneViews[FRAME_DATA_INDEX].postProcess; } + inline const Camera& GetActiveCamera() const { return mCameras[mIndex_SelectedCamera]; } + inline Camera& GetActiveCamera() { return mCameras[mIndex_SelectedCamera]; } + + // Mesh, Model, GameObj management + //TransformID CreateTransform(Transform** ppTransform); + //GameObject* CreateObject(TransformID tfID, ModelID modelID); + MeshID AddMesh(Mesh&& mesh); + MeshID AddMesh(const Mesh& mesh); + ModelID CreateModel(); + MaterialID CreateMaterial(const std::string& UniqueMaterialName); + + Material& GetMaterial(MaterialID ID); + Model& GetModel(ModelID); + +//---------------------------------------------------------------------------------------------------------------- +// SCENE DATA +//---------------------------------------------------------------------------------------------------------------- +protected: + using MeshLookup_t = std::unordered_map; + using ModelLookup_t = std::unordered_map; + using MaterialLookup_t = std::unordered_map; + //-------------------------------------------------------------- + + // + // SCENE VIEWS + // + std::vector mFrameSceneViews; + + // + // SCENE RESOURCE CONTAINERS + // + MeshLookup_t mMeshes; + ModelLookup_t mModels; + MaterialLookup_t mMaterials; + std::vector mpObjects; + std::vector mpTransforms; + std::vector mCameras; + + Light mDirectionalLight; + std::vector mLightsStatic; // stationary lights + std::vector mLightsDynamic; // moving lights + //Skybox mSkybox; + + + // + // DATA + // + BoundingBox mSceneBoundingBox; + std::vector mMeshBoundingBoxes; + std::vector mGameObjectBoundingBoxes; + MaterialID mDefaultMaterialID; + + + // + // SCENE STATE + // + int mIndex_SelectedCamera; + +public: + int mIndex_ActiveEnvironmentMapPreset; + //EEnvironmentMapPresets mActiveSkyboxPreset; + //Settings::SceneRender mSceneRenderSettings; + + +protected: + const Input& mInput; + const std::unique_ptr& mpWindow; + VQEngine& mEngine; + const FResourceNames& mResourceNames; + AssetLoader& mAssetLoader; + VQRenderer& mRenderer; + + FSceneRepresentation mSceneRepresentation; + +//---------------------------------------------------------------------------------------------------------------- +// INTERNAL DATA +//---------------------------------------------------------------------------------------------------------------- +private: + MemoryPool mGameObjectPool; + MemoryPool mTransformPool; + + std::mutex mMtx_Meshes; + std::mutex mMtx_Models; + std::mutex mMtx_Materials; + + AssetLoader::ModelLoadResults_t mModelLoadResults; + AssetLoader::FMaterialTextureAssignments mMaterialAssignments; + + // cache + std::unordered_map mLoadedMaterials; + + //CPUProfiler* mpCPUProfiler; + //ModelLoader mModelLoader; + //MaterialPool mMaterials; + //ModelLoadQueue mModelLoadQueue; + + //BoundingBox mSceneBoundingBox; + //FSceneView mSceneView; + //ShadowView mShadowView; + +}; diff --git a/Source/Application/Settings.h b/Source/Application/Settings.h index abacb1f0..8af5a9d5 100644 --- a/Source/Application/Settings.h +++ b/Source/Application/Settings.h @@ -18,6 +18,8 @@ #pragma once +#include + enum EDisplayMode { WINDOWED = 0, @@ -60,4 +62,6 @@ struct FEngineSettings bool bAutomatedTestRun = false; int NumAutomatedTestFrames = -1; + + std::string StartupScene; }; \ No newline at end of file diff --git a/Source/Application/Types.h b/Source/Application/Types.h index f7b4a616..65b8da86 100644 --- a/Source/Application/Types.h +++ b/Source/Application/Types.h @@ -46,4 +46,10 @@ using CBV_ID = ID_TYPE; using RTV_ID = ID_TYPE; using DSV_ID = ID_TYPE; -using EnvironmentMapID = ID_TYPE; \ No newline at end of file +using ShaderID = ID_TYPE; + +using EnvironmentMapID = ID_TYPE; +using MeshID = ID_TYPE; +using MaterialID = ID_TYPE; +using ModelID = ID_TYPE; +using TransformID = ID_TYPE; \ No newline at end of file diff --git a/Source/Application/VQEngine.h b/Source/Application/VQEngine.h index 9a68902b..0791f79b 100644 --- a/Source/Application/VQEngine.h +++ b/Source/Application/VQEngine.h @@ -27,6 +27,8 @@ #include "Transform.h" #include "Camera.h" #include "Input.h" +#include "AssetLoader.h" +#include "Scene.h" #include "Libs/VQUtils/Source/Multithreading.h" #include "Libs/VQUtils/Source/Timer.h" @@ -38,45 +40,22 @@ // Outputs Render/Update thread sync values on each Tick() #define DEBUG_LOG_THREAD_SYNC_VERBOSE 0 - // // DATA STRUCTS // -struct FPostProcessParameters -{ - EColorSpace ContentColorSpace = EColorSpace::REC_709; - EDisplayCurve OutputDisplayCurve = EDisplayCurve::sRGB; - float DisplayReferenceBrightnessLevel = 200.0f; - int ToggleGammaCorrection = 1; -}; -struct FFrameData +struct FLoadingScreenData { std::array SwapChainClearColor; - // scene - Camera SceneCamera; - Transform TFCube; - bool bCubeAnimating; + int SelectedLoadingScreenSRVIndex = INVALID_ID; + std::mutex Mtx; + std::vector SRVs; + + SRV_ID GetSelectedLoadingScreenSRV_ID() const; + void RotateLoadingScreenImage(); - // post process - FPostProcessParameters PPParams; -}; -struct FLoadingScreenData -{ - std::array SwapChainClearColor; - - SRV_ID SRVLoadingScreen = INVALID_ID; // TODO: animation resources }; -class IWindowUpdateContext -{ -public: - HWND hwnd; - std::vector mFrameData; - std::vector mLoadingScreenData; -}; -class MainWindowSceneData : public IWindowUpdateContext{}; -class DebugWindowSceneData : public IWindowUpdateContext{}; struct FEnvironmentMapDescriptor @@ -107,13 +86,6 @@ struct FEnvironmentMap int MaxFrameAverageLightLevel = 0; }; -struct FSceneRepresentation -{ - std::string SceneName; - std::string EnvironmentMapPreset; - // TBA -}; - struct FRenderingResources{}; struct FRenderingResources_MainWindow : public FRenderingResources { @@ -149,7 +121,13 @@ enum EAppState NUM_APP_STATES }; - +using BuiltinMeshNameArray_t = std::array; +struct FResourceNames +{ + BuiltinMeshNameArray_t mBuiltinMeshNames; + std::vector mEnvironmentMapPresetNames; + std::vector mSceneNames; +}; // // VQENGINE @@ -157,9 +135,7 @@ enum EAppState class VQEngine : public IWindowOwner { public: - -public: - + VQEngine(); // --------------------------------------------------------- // Main Thread // --------------------------------------------------------- @@ -207,7 +183,7 @@ class VQEngine : public IWindowOwner void RenderThread_PreRender(); // RENDER() - // - Records command lists in parallel per SceneView + // - Records command lists in parallel per FSceneView // - Submits commands to the GPU // - Presents SwapChain void RenderThread_Render(); @@ -225,12 +201,12 @@ class VQEngine : public IWindowOwner void UpdateThread_WaitForRenderThread(); void UpdateThread_SignalRenderThread(); - // PRE_UPDATE() + // PreUpdate() // - Updates timer // - Updates input state reading from Main Thread's input queue void UpdateThread_PreUpdate(float& dt); - // UPDATE() + // Update() // - Updates program state (init/load/sim/unload/exit) // - Starts loading tasks // - Animates loading screen @@ -239,9 +215,8 @@ class VQEngine : public IWindowOwner void UpdateThread_UpdateScene_MainWnd(const float dt); void UpdateThread_UpdateScene_DebugWnd(const float dt); - - // POST_UPDATE() - // - Computes visibility per SceneView + // PostUpdate() + // - Computes visibility per FSceneView void UpdateThread_PostUpdate(); @@ -253,16 +228,29 @@ class VQEngine : public IWindowOwner inline const std::string& GetWindowName(const std::unique_ptr& pWin) const { return GetWindowName(pWin->GetHWND()); } inline const std::string& GetWindowName(const Window* pWin) const { return GetWindowName(pWin->GetHWND()); } + + // --------------------------------------------------------- + // Scene Interface + // --------------------------------------------------------- + void StartLoadingEnvironmentMap(int IndexEnvMap); + void StartLoadingScene(int IndexScene); + + void UnloadEnvironmentMap(); + + // Getters + MeshID GetBuiltInMeshID(const std::string& MeshName) const; + + inline const FResourceNames& GetResourceNames() const { return mResourceNames; } + inline AssetLoader& GetAssetLoader() { return mAssetLoader; } + + private: //------------------------------------------------------------------------------------------------- - using BuiltinMeshArray_t = std::array; - using BuiltinMeshNameArray_t = std::array; - using EnvironmentMapLookup_t = std::unordered_map; + using EnvironmentMapDescLookup_t = std::unordered_map; //------------------------------------------------------------------------------------------------- using EventPtr_t = std::shared_ptr; using EventQueue_t = BufferedContainer, EventPtr_t>; //------------------------------------------------------------------------------------------------- - using UpdateContextLookup_t = std::unordered_map; using RenderingResourcesLookup_t = std::unordered_map>; using WindowLookup_t = std::unordered_map>; using WindowNameLookup_t = std::unordered_map; @@ -273,7 +261,8 @@ class VQEngine : public IWindowOwner std::thread mUpdateThread; ThreadPool mWorkers_Update; ThreadPool mWorkers_Render; - ThreadPool mWorkers_Load; + ThreadPool mWorkers_ModelLoading; + ThreadPool mWorkers_TextureLoading; // sync std::atomic mbStopAllThreads; @@ -298,24 +287,27 @@ class VQEngine : public IWindowOwner EventQueue_t mEventQueue_WinToVQE_Update; EventQueue_t mEventQueue_VQEToWin_Main; - // render VQRenderer mRenderer; + AssetLoader mAssetLoader; - // data + // data: geometry BuiltinMeshArray_t mBuiltinMeshes; - BuiltinMeshNameArray_t mBuiltinMeshNames; + + // data: environment maps & HDR profiles std::vector mDisplayHDRProfiles; - EnvironmentMapLookup_t mLookup_EnvironmentMapDescriptors; - std::vector mEnvironmentMapPresetNames; - int mActiveEnvironmentMapPresetIndex; + EnvironmentMapDescLookup_t mLookup_EnvironmentMapDescriptors; + + // data: strings + FResourceNames mResourceNames; // state + EAppState mAppState; std::atomic mbRenderThreadInitialized; std::atomic mNumRenderLoopsExecuted; std::atomic mNumUpdateLoopsExecuted; std::atomic mbLoadingLevel; - EAppState mAppState; + std::atomic mbLoadingEnvironmentMap; std::atomic mbMainWindowHDRTransitionInProgress; // see DispatchHDRSwapchainTransitionEvents() // system & settings @@ -323,10 +315,11 @@ class VQEngine : public IWindowOwner VQSystemInfo::FSystemInfo mSysInfo; // scene - MainWindowSceneData mScene_MainWnd; - DebugWindowSceneData mScene_DebugWnd; - UpdateContextLookup_t mWindowUpdateContextLookup; - std::queue mQueue_SceneLoad; + FLoadingScreenData mLoadingScreenData; + std::queue mQueue_SceneLoad; + + int mIndex_SelectedScene; + std::unique_ptr mpScene; #if 0 RenderingResourcesLookup_t mRenderingResources; @@ -335,6 +328,7 @@ class VQEngine : public IWindowOwner FRenderingResources_DebugWindow mResources_DebugWnd; #endif + // timer / profiler Timer mTimer; Timer mTimerRender; @@ -347,11 +341,12 @@ class VQEngine : public IWindowOwner private: + void InitializeInput(); void InitializeEngineSettings(const FStartupParameters& Params); void InitializeWindows(const FStartupParameters& Params); void InitializeHDRProfiles(); void InitializeEnvironmentMaps(); - void InitializeScenes(const FStartupParameters& Params); + void InitializeScenes(); void InitializeThreads(); void ExitThreads(); @@ -386,16 +381,17 @@ class VQEngine : public IWindowOwner // void TransitionForSceneRendering(FWindowRenderContext& ctx); void RenderShadowMaps(FWindowRenderContext& ctx); - void RenderSceneColor(FWindowRenderContext& ctx, const FFrameData& FrameData); + void RenderSceneColor(FWindowRenderContext& ctx, const FSceneView& SceneView); void ResolveMSAA(FWindowRenderContext& ctx); void TransitionForPostProcessing(FWindowRenderContext& ctx); void RenderPostProcess(FWindowRenderContext& ctx, const FPostProcessParameters& PPParams); void RenderUI(FWindowRenderContext& ctx); - HRESULT PresentFrame(FWindowRenderContext& ctx); void CompositUIToHDRSwapchain(FWindowRenderContext& ctx); // TODO + HRESULT PresentFrame(FWindowRenderContext& ctx); // temp - struct FrameConstantBuffer { DirectX::XMMATRIX matModelViewProj; }; + struct FFrameConstantBuffer { DirectX::XMMATRIX matModelViewProj; }; + struct FFrameConstantBuffer2 { DirectX::XMMATRIX matModelViewProj; int iTextureConfig; int iTextureOutput; }; void DrawMesh(ID3D12GraphicsCommandList* pCmd, const Mesh& mesh); @@ -404,7 +400,6 @@ class VQEngine : public IWindowOwner const std::unique_ptr& GetWindow(HWND hwnd) const; const FWindowSettings& GetWindowSettings(HWND hwnd) const; FWindowSettings& GetWindowSettings(HWND hwnd); - FFrameData& GetCurrentFrameData(HWND hwnd); const FEnvironmentMapDescriptor& GetEnvironmentMapDesc(const std::string& EnvMapName) const; @@ -418,18 +413,22 @@ class VQEngine : public IWindowOwner bool IsWindowRegistered(HWND hwnd) const; bool ShouldRenderHDR(HWND hwnd) const; - void CalculateEffectiveFrameRate(HWND hwnd); + void CalculateEffectiveFrameRateLimit(HWND hwnd); const FDisplayHDRProfile* GetHDRProfileIfExists(const wchar_t* pwStrLogicalDisplayName); FSetHDRMetaDataParams GatherHDRMetaDataParameters(HWND hwnd); + // Busy waits until render thread catches up with update thread + void WaitUntilRenderingFinishes(); + private: // Reads EngineSettings.ini from next to the executable and returns a // FStartupParameters struct as it readily has override booleans for engine settings - static FStartupParameters ParseEngineSettingsFile(); - static std::vector ParseEnvironmentMapsFile(); - static std::vector ParseHDRProfilesFile(); - static std::vector ParseScenesFile(); + static FStartupParameters ParseEngineSettingsFile(); + static std::vector> ParseSceneIndexMappingFile(); + static std::vector ParseEnvironmentMapsFile(); + static std::vector ParseHDRProfilesFile(); + static FSceneRepresentation ParseSceneFile(const std::string& SceneFile); public: // Supported HDR Formats { DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R16G16B16A16_FLOAT } diff --git a/Source/Application/VQEngine_EventHandlers.cpp b/Source/Application/VQEngine_EventHandlers.cpp index fe616df3..eeb1ef28 100644 --- a/Source/Application/VQEngine_EventHandlers.cpp +++ b/Source/Application/VQEngine_EventHandlers.cpp @@ -184,21 +184,16 @@ void VQEngine::UpdateThread_HandleWindowResizeEvent(const std::shared_ptr p = std::static_pointer_cast(pEvent); - // TODO: potentially use a lookup when camera count gets larger. - // also need to take into account the array of framedata that containts the camera. - // there's only one camera for one window for now. if (p->hwnd == mpWinMain->GetHWND()) { - if (mWindowUpdateContextLookup.find(p->hwnd) != mWindowUpdateContextLookup.end()) - { - Camera& cam = GetCurrentFrameData(p->hwnd).SceneCamera; - - ProjectionMatrixParameters UpdatedProjectionMatrixParams = cam.mProjParams; - UpdatedProjectionMatrixParams.ViewporWidth = static_cast(p->width ); - UpdatedProjectionMatrixParams.ViewporHeight = static_cast(p->height); + // TODO: all cameras? + Camera& cam = mpScene->GetActiveCamera(); + + FProjectionMatrixParameters UpdatedProjectionMatrixParams = cam.GetProjectionParameters(); + UpdatedProjectionMatrixParams.ViewportWidth = static_cast(p->width ); + UpdatedProjectionMatrixParams.ViewportHeight = static_cast(p->height); - cam.SetProjectionMatrix(UpdatedProjectionMatrixParams); - } + cam.SetProjectionMatrix(UpdatedProjectionMatrixParams); } } @@ -287,7 +282,7 @@ void VQEngine::RenderThread_HandleWindowResizeEvent(const std::shared_ptrGatherHDRMetaDataParameters(hwnd); if (pWnd->GetIsOnHDRCapableDisplay()) Swapchain.SetHDRMetaData(HDRMetaData); - else + else if (GetWindowSettings(hwnd).bEnableHDR) Swapchain.ClearHDRMetaData(); } Swapchain.EnsureSwapChainColorSpace(Swapchain.GetFormat() == DXGI_FORMAT_R16G16B16A16_FLOAT ? _16 : _8, false); @@ -439,7 +434,7 @@ void VQEngine::RenderThread_HandleSetVSyncEvent(const IEvent* pEvent) const FSetHDRMetaDataParams HDRMetaData = this->GatherHDRMetaDataParameters(hwnd); if (pWnd->GetIsOnHDRCapableDisplay()) Swapchain.SetHDRMetaData(HDRMetaData); - else + else if (GetWindowSettings(hwnd).bEnableHDR) Swapchain.ClearHDRMetaData(); Swapchain.EnsureSwapChainColorSpace(Swapchain.GetFormat() == DXGI_FORMAT_R16G16B16A16_FLOAT ? _16 : _8, false); } @@ -466,7 +461,7 @@ void VQEngine::RenderThread_HandleSetSwapchainFormatEvent(const IEvent* pEvent) { if (pWnd->GetIsOnHDRCapableDisplay()) Swapchain.SetHDRMetaData(HDRMetaData); - else + else if(GetWindowSettings(hwnd).bEnableHDR) Swapchain.ClearHDRMetaData(); Swapchain.EnsureSwapChainColorSpace(pSwapchainEvent->format == DXGI_FORMAT_R16G16B16A16_FLOAT? _16 : _8, false); } @@ -477,7 +472,7 @@ void VQEngine::RenderThread_HandleSetSwapchainFormatEvent(const IEvent* pEvent) const int BACK_BUFFER_INDEX = Swapchain.GetCurrentBackBufferIndex(); const EDisplayCurve OutputDisplayCurve = Swapchain.IsHDRFormat() ? EDisplayCurve::Linear : EDisplayCurve::sRGB; for (int i = 0; i < NUM_BACK_BUFFERS; ++i) - mScene_MainWnd.mFrameData[i].PPParams.OutputDisplayCurve = OutputDisplayCurve; + mpScene->GetPostProcessParameters(i).OutputDisplayCurve = OutputDisplayCurve; Log::Info("Set Swapchain Format: %s | OutputDisplayCurve: %s" , VQRenderer::DXGIFormatAsString(pSwapchainEvent->format).data() diff --git a/Source/Application/VQEngine_Main.cpp b/Source/Application/VQEngine_Main.cpp index 53f6ed06..4788659f 100644 --- a/Source/Application/VQEngine_Main.cpp +++ b/Source/Application/VQEngine_Main.cpp @@ -18,9 +18,6 @@ #include "VQEngine.h" -#include "Libs/VQUtils/Source/utils.h" - -#include #include #ifdef _DEBUG @@ -28,43 +25,7 @@ constexpr char* BUILD_CONFIG = "-Debug"; #else constexpr char* BUILD_CONFIG = ""; #endif -constexpr char* VQENGINE_VERSION = "v0.4.0"; - - -static std::pair ParseLineINI(const std::string& iniLine, bool* pbSectionTag) -{ - assert(!iniLine.empty()); - std::pair SettingNameValuePair; - - const bool bSectionTag = iniLine[0] == '['; - if(pbSectionTag) - *pbSectionTag = bSectionTag; - - if (bSectionTag) - { - auto vecSettingNameValue = StrUtil::split(iniLine.substr(1), ']'); - SettingNameValuePair.first = vecSettingNameValue[0]; - } - else - { - auto vecSettingNameValue = StrUtil::split(iniLine, '='); - assert(vecSettingNameValue.size() >= 2); - SettingNameValuePair.first = vecSettingNameValue[0]; - SettingNameValuePair.second = vecSettingNameValue[1]; - } - - return SettingNameValuePair; -} - -static std::unordered_map S_LOOKUP_STR_TO_DISPLAYMODE = -{ - { "Fullscreen" , EDisplayMode::EXCLUSIVE_FULLSCREEN } - , { "Borderless" , EDisplayMode::BORDERLESS_FULLSCREEN } - , { "BorderlessFullscreen" , EDisplayMode::BORDERLESS_FULLSCREEN } - , { "BorderlessWindowed" , EDisplayMode::BORDERLESS_FULLSCREEN } - , { "Windowed" , EDisplayMode::WINDOWED } -}; - +constexpr char* VQENGINE_VERSION = "v0.5.0"; #define REPORT_SYSTEM_INFO 1 @@ -75,7 +36,9 @@ void ReportSystemInfo(const VQSystemInfo::FSystemInfo& i, bool bDetailed = false Log::Info("\n%s", sysInfo.c_str()); } #endif - +VQEngine::VQEngine() + : mAssetLoader(mWorkers_ModelLoading, mWorkers_TextureLoading, mRenderer) +{} void VQEngine::MainThread_Tick() { @@ -100,30 +63,37 @@ bool VQEngine::Initialize(const FStartupParameters& Params) InitializeEngineSettings(Params); InitializeEnvironmentMaps(); InitializeHDRProfiles(); - InitializeScenes(Params); - float f2 = t.Tick(); + float f1 = t.Tick(); InitializeWindows(Params); float f3 = t.Tick(); + InitializeInput(); + InitializeScenes(); + float f2 = t.Tick(); InitializeThreads(); - CalculateEffectiveFrameRate(mpWinMain->GetHWND()); + CalculateEffectiveFrameRateLimit(mpWinMain->GetHWND()); float f4 = t.Tick(); // offload system info acquisition to a thread as it takes a few seconds on Debug build mWorkers_Update.AddTask([&]() { this->mSysInfo = VQSystemInfo::GetSystemInfo(); + #if REPORT_SYSTEM_INFO ReportSystemInfo(this->mSysInfo); #endif HWND hwnd = mpWinMain->GetHWND(); - mEventQueue_WinToVQE_Renderer.AddItem(std::make_shared(hwnd, this->GatherHDRMetaDataParameters(hwnd))); + if (!mpWinMain->IsClosed()) + { + mEventQueue_WinToVQE_Renderer.AddItem(std::make_shared(hwnd, this->GatherHDRMetaDataParameters(hwnd))); + } }); float f0 = t.Tick(); #if 0 Log::Info("[PERF] VQEngine::Initialize() : %.3fs", t2.StopGetDeltaTimeAndReset()); Log::Info("[PERF] DispatchSysInfo : %.3fs", f0); - Log::Info("[PERF] Settings : %.3fs", f2); + Log::Info("[PERF] Settings : %.3fs", f1); + Log::Info("[PERF] Scenes : %.3fs", f2); Log::Info("[PERF] Windows : %.3fs", f3); Log::Info("[PERF] Threads : %.3fs", f4); #endif @@ -140,6 +110,17 @@ void VQEngine::Exit() +void VQEngine::InitializeInput() +{ +#if ENABLE_RAW_INPUT + Input::InitRawInputDevices(mpWinMain->GetHWND()); +#endif + + // initialize input states + RegisterWindowForInput(mpWinMain); + if (mpWinDebug) RegisterWindowForInput(mpWinDebug); +} + void VQEngine::InitializeEngineSettings(const FStartupParameters& Params) { const FEngineSettings& p = Params.EngineSettings; @@ -168,12 +149,13 @@ void VQEngine::InitializeEngineSettings(const FStartupParameters& Params) s.bAutomatedTestRun = false; s.NumAutomatedTestFrames = 100; // default num frames to run if -Test is specified in cmd line params + s.StartupScene = "Default"; // Override #0 : from file FStartupParameters paramFile = VQEngine::ParseEngineSettingsFile(); const FEngineSettings& pf = paramFile.EngineSettings; - if (paramFile.bOverrideGFXSetting_bVSync ) s.gfx.bVsync = pf.gfx.bVsync; - if (paramFile.bOverrideGFXSetting_bAA ) s.gfx.bAntiAliasing = pf.gfx.bAntiAliasing; + if (paramFile.bOverrideGFXSetting_bVSync) s.gfx.bVsync = pf.gfx.bVsync; + if (paramFile.bOverrideGFXSetting_bAA) s.gfx.bAntiAliasing = pf.gfx.bAntiAliasing; if (paramFile.bOverrideGFXSetting_bUseTripleBuffering) s.gfx.bUseTripleBuffering = pf.gfx.bUseTripleBuffering; if (paramFile.bOverrideGFXSetting_RenderScale) s.gfx.RenderScale = pf.gfx.RenderScale; if (paramFile.bOverrideGFXSetting_bMaxFrameRate) s.gfx.MaxFrameRate = pf.gfx.MaxFrameRate; @@ -197,13 +179,14 @@ void VQEngine::InitializeEngineSettings(const FStartupParameters& Params) s.NumAutomatedTestFrames = pf.NumAutomatedTestFrames; } + if (paramFile.bOverrideENGSetting_StartupScene) s.StartupScene = pf.StartupScene; // Override #1 : if there's command line params - if (Params.bOverrideGFXSetting_bVSync ) s.gfx.bVsync = p.gfx.bVsync; - if (Params.bOverrideGFXSetting_bAA ) s.gfx.bAntiAliasing = p.gfx.bAntiAliasing; - if (Params.bOverrideGFXSetting_bUseTripleBuffering) s.gfx.bUseTripleBuffering = p.gfx.bUseTripleBuffering; - if (Params.bOverrideGFXSetting_RenderScale) s.gfx.RenderScale = p.gfx.RenderScale; - if (Params.bOverrideGFXSetting_bMaxFrameRate) s.gfx.MaxFrameRate = p.gfx.MaxFrameRate; + if (Params.bOverrideGFXSetting_bVSync) s.gfx.bVsync = p.gfx.bVsync; + if (Params.bOverrideGFXSetting_bAA) s.gfx.bAntiAliasing = p.gfx.bAntiAliasing; + if (Params.bOverrideGFXSetting_bUseTripleBuffering) s.gfx.bUseTripleBuffering = p.gfx.bUseTripleBuffering; + if (Params.bOverrideGFXSetting_RenderScale) s.gfx.RenderScale = p.gfx.RenderScale; + if (Params.bOverrideGFXSetting_bMaxFrameRate) s.gfx.MaxFrameRate = p.gfx.MaxFrameRate; if (Params.bOverrideENGSetting_MainWindowWidth) s.WndMain.Width = p.WndMain.Width; if (Params.bOverrideENGSetting_MainWindowHeight) s.WndMain.Height = p.WndMain.Height; @@ -211,20 +194,20 @@ void VQEngine::InitializeEngineSettings(const FStartupParameters& Params) if (Params.bOverrideENGSetting_PreferredDisplay) s.WndMain.PreferredDisplay = p.WndMain.PreferredDisplay; if (Params.bOverrideGFXSetting_bHDR) s.WndMain.bEnableHDR = p.WndMain.bEnableHDR; - if (Params.bOverrideENGSetting_bDebugWindowEnable) s.bShowDebugWindow = p.bShowDebugWindow; - if (Params.bOverrideENGSetting_DebugWindowWidth) s.WndDebug.Width = p.WndDebug.Width; - if (Params.bOverrideENGSetting_DebugWindowHeight) s.WndDebug.Height = p.WndDebug.Height; - if (Params.bOverrideENGSetting_DebugWindowDisplayMode) s.WndDebug.DisplayMode = p.WndDebug.DisplayMode; - if (Params.bOverrideENGSetting_DebugWindowPreferredDisplay) s.WndDebug.PreferredDisplay = p.WndDebug.PreferredDisplay; + if (Params.bOverrideENGSetting_bDebugWindowEnable) s.bShowDebugWindow = p.bShowDebugWindow; + if (Params.bOverrideENGSetting_DebugWindowWidth) s.WndDebug.Width = p.WndDebug.Width; + if (Params.bOverrideENGSetting_DebugWindowHeight) s.WndDebug.Height = p.WndDebug.Height; + if (Params.bOverrideENGSetting_DebugWindowDisplayMode) s.WndDebug.DisplayMode = p.WndDebug.DisplayMode; + if (Params.bOverrideENGSetting_DebugWindowPreferredDisplay) s.WndDebug.PreferredDisplay= p.WndDebug.PreferredDisplay; - if (Params.bOverrideENGSetting_bAutomatedTest) s.bAutomatedTestRun = p.bAutomatedTestRun; + if (Params.bOverrideENGSetting_bAutomatedTest) s.bAutomatedTestRun = p.bAutomatedTestRun; if (Params.bOverrideENGSetting_bTestFrames) { s.bAutomatedTestRun = true; - s.NumAutomatedTestFrames = Params.EngineSettings.NumAutomatedTestFrames; + s.NumAutomatedTestFrames = p.NumAutomatedTestFrames; } - // TODO: parse scene to load + if (Params.bOverrideENGSetting_StartupScene) s.StartupScene = p.StartupScene; } void VQEngine::InitializeWindows(const FStartupParameters& Params) @@ -272,22 +255,34 @@ void VQEngine::InitializeEnvironmentMaps() for (const FEnvironmentMapDescriptor& desc : descs) { mLookup_EnvironmentMapDescriptors[desc.Name] = desc; - mEnvironmentMapPresetNames.push_back(desc.Name); + mResourceNames.mEnvironmentMapPresetNames.push_back(desc.Name); } } -void VQEngine::InitializeScenes(const FStartupParameters& Params) +void VQEngine::InitializeScenes() { - std::vector< FSceneRepresentation> SceneReps = VQEngine::ParseScenesFile(); + std::vector& mSceneNames = mResourceNames.mSceneNames; + + // Read Scene Index Mappings from file and initialize @mSceneNames + { + std::vector> SceneIndexMappings = VQEngine::ParseSceneIndexMappingFile(); + for (auto& nameIndex : SceneIndexMappings) + mSceneNames.push_back(std::move(nameIndex.first)); + } - auto it = std::find_if(SceneReps.begin(), SceneReps.end(), [&Params](const FSceneRepresentation& s) { return s.SceneName == Params.LevelNameToLoad; }); - const bool bSceneFound = it != SceneReps.end(); + // read scene files from disk: Data/Scenes/ + + // set the selected scene index + auto it2 = std::find_if(mSceneNames.begin(), mSceneNames.end(), [&](const std::string& scn) { return scn == mSettings.StartupScene; }); + bool bSceneFound = it2 != mSceneNames.end(); if (!bSceneFound) { - Log::Error("Couldn't find scene '%s' among parsed scene files. DefaultScene will be loaded", Params.LevelNameToLoad.c_str()); + Log::Error("Couldn't find scene '%s' among scene file names", mSettings.StartupScene.c_str()); + it2 = mSceneNames.begin(); } - - mQueue_SceneLoad.push(bSceneFound ? *it : SceneReps.back()); + mIndex_SelectedScene = static_cast(it2 - mSceneNames.begin()); + + this->StartLoadingScene(mIndex_SelectedScene); } void VQEngine::InitializeThreads() @@ -295,22 +290,27 @@ void VQEngine::InitializeThreads() const int NUM_SWAPCHAIN_BACKBUFFERS = mSettings.gfx.bUseTripleBuffering ? 3 : 2; const size_t HWThreads = ThreadPool::sHardwareThreadCount; const size_t HWCores = HWThreads / 2; - const size_t NumWorkers = HWCores - 2; // reserve 2 cores for (Update + Render) + Main threads + const size_t NumRuntimeWorkers = HWCores - 2; // reserve 2 cores for Update + Render threads + const size_t NumLoadtimeWorkers = HWThreads; mpSemUpdate.reset(new Semaphore(NUM_SWAPCHAIN_BACKBUFFERS, NUM_SWAPCHAIN_BACKBUFFERS)); mpSemRender.reset(new Semaphore(0 , NUM_SWAPCHAIN_BACKBUFFERS)); - + + mbRenderThreadInitialized.store(false); mbStopAllThreads.store(false); - mWorkers_Load.Initialize(NumWorkers); + + mWorkers_ModelLoading.Initialize(NumLoadtimeWorkers, "LoadWorkers_Model"); + mWorkers_TextureLoading.Initialize(NumLoadtimeWorkers, "LoadWorkers_Texture"); mRenderThread = std::thread(&VQEngine::RenderThread_Main, this); mUpdateThread = std::thread(&VQEngine::UpdateThread_Main, this); - mWorkers_Update.Initialize(NumWorkers); - mWorkers_Render.Initialize(NumWorkers); + mWorkers_Update.Initialize(NumRuntimeWorkers, "UpdateWorkers"); + mWorkers_Render.Initialize(NumRuntimeWorkers, "RenderWorkers"); } void VQEngine::ExitThreads() { - mWorkers_Load.Exit(); + mWorkers_ModelLoading.Exit(); + mWorkers_TextureLoading.Exit(); mbStopAllThreads.store(true); mRenderThread.join(); mUpdateThread.join(); @@ -375,10 +375,13 @@ FWindowSettings& VQEngine::GetWindowSettings(HWND hwnd) void VQEngine::RegisterWindowForInput(const std::unique_ptr& pWnd) { - if (pWnd) + if (!pWnd) { - mInputStates.emplace(pWnd->GetHWND(), std::move(Input())); + Log::Warning("RegisterWindowForInput() called with nullptr"); + return; } + + mInputStates.emplace(pWnd->GetHWND(), std::move(Input())); } void VQEngine::UnregisterWindowForInput(const std::unique_ptr& pWnd) @@ -397,313 +400,3 @@ void VQEngine::UnregisterWindowForInput(const std::unique_ptr& pWnd) } } - - -FStartupParameters VQEngine::ParseEngineSettingsFile() -{ - constexpr char* ENGINE_SETTINGS_FILE_NAME = "Data/EngineSettings.ini"; - FStartupParameters params = {}; - - std::ifstream file(ENGINE_SETTINGS_FILE_NAME); - if (file.is_open()) - { - std::string line; - std::string currSection; - bool bReadingSection = false; - while (std::getline(file, line)) - { - if (line[0] == ';') continue; // skip comment lines - if (line == "") continue; // skip empty lines - - const std::pair SettingNameValuePair = ParseLineINI(line, &bReadingSection); - const std::string& SettingName = SettingNameValuePair.first; - const std::string& SettingValue = SettingNameValuePair.second; - - // Header sections; - if (bReadingSection) - { - currSection = SettingName; - continue; - } - - - // - // Graphics - // - if (SettingName == "VSync") - { - params.bOverrideGFXSetting_bVSync = true; - params.EngineSettings.gfx.bVsync = StrUtil::ParseBool(SettingValue); - } - if (SettingName == "RenderScale") - { - params.bOverrideGFXSetting_RenderScale = true; - params.EngineSettings.gfx.RenderScale = StrUtil::ParseFloat(SettingValue); - } - if (SettingName == "TripleBuffer") - { - params.bOverrideGFXSetting_bUseTripleBuffering = true; - params.EngineSettings.gfx.bUseTripleBuffering = StrUtil::ParseBool(SettingValue); - } - if (SettingName == "AntiAliasing" || SettingName == "AA") - { - params.bOverrideGFXSetting_bAA = true; - params.EngineSettings.gfx.bAntiAliasing = StrUtil::ParseBool(SettingValue); - } - if (SettingName == "MaxFrameRate" || SettingName == "MaxFPS") - { - params.bOverrideGFXSetting_bMaxFrameRate = true; - if (SettingValue == "Unlimited" || SettingValue == "0") - params.EngineSettings.gfx.MaxFrameRate = 0; - else if (SettingValue == "Auto" || SettingValue == "Automatic" || SettingValue == "-1") - params.EngineSettings.gfx.MaxFrameRate = -1; - else - params.EngineSettings.gfx.MaxFrameRate = StrUtil::ParseInt(SettingValue); - } - - // - // Engine - // - if (SettingName == "Width") - { - params.bOverrideENGSetting_MainWindowWidth = true; - params.EngineSettings.WndMain.Width = StrUtil::ParseInt(SettingValue); - } - if (SettingName == "Height") - { - params.bOverrideENGSetting_MainWindowHeight = true; - params.EngineSettings.WndMain.Height = StrUtil::ParseInt(SettingValue); - } - if (SettingName == "DisplayMode") - { - if (S_LOOKUP_STR_TO_DISPLAYMODE.find(SettingValue) != S_LOOKUP_STR_TO_DISPLAYMODE.end()) - { - params.bOverrideENGSetting_bDisplayMode = true; - params.EngineSettings.WndMain.DisplayMode = S_LOOKUP_STR_TO_DISPLAYMODE.at(SettingValue); - } - } - if (SettingName == "PreferredDisplay") - { - params.bOverrideENGSetting_PreferredDisplay = true; - params.EngineSettings.WndMain.PreferredDisplay = StrUtil::ParseInt(SettingValue); - } - if (SettingName == "HDR") - { - params.bOverrideGFXSetting_bHDR = true; - params.EngineSettings.WndMain.bEnableHDR = StrUtil::ParseBool(SettingValue); - } - - - if (SettingName == "DebugWindow") - { - params.bOverrideENGSetting_bDebugWindowEnable = true; - params.EngineSettings.bShowDebugWindow = StrUtil::ParseBool(SettingValue); - } - if (SettingName == "DebugWindowWidth") - { - params.bOverrideENGSetting_DebugWindowWidth = true; - params.EngineSettings.WndDebug.Width = StrUtil::ParseInt(SettingValue); - } - if (SettingName == "DebugWindowHeight") - { - params.bOverrideENGSetting_DebugWindowHeight = true; - params.EngineSettings.WndDebug.Height = StrUtil::ParseInt(SettingValue); - } - if (SettingName == "DebugWindowDisplayMode") - { - if (S_LOOKUP_STR_TO_DISPLAYMODE.find(SettingValue) != S_LOOKUP_STR_TO_DISPLAYMODE.end()) - { - params.bOverrideENGSetting_DebugWindowDisplayMode = true; - params.EngineSettings.WndDebug.DisplayMode = S_LOOKUP_STR_TO_DISPLAYMODE.at(SettingValue); - } - } - if (SettingName == "DebugWindowPreferredDisplay") - { - params.bOverrideENGSetting_DebugWindowPreferredDisplay = true; - params.EngineSettings.WndDebug.PreferredDisplay = StrUtil::ParseInt(SettingValue); - } - - } - } - else - { - Log::Warning("Cannot find settings file %s in current directory: %s", ENGINE_SETTINGS_FILE_NAME, DirectoryUtil::GetCurrentPath().c_str()); - Log::Warning("Will use default settings for Engine & Graphics.", ENGINE_SETTINGS_FILE_NAME, DirectoryUtil::GetCurrentPath().c_str()); - } - - return params; -} - -std::vector VQEngine::ParseEnvironmentMapsFile() -{ - constexpr char* SETTINGS_FILE_NAME = "Data/EnvironmentMaps.ini"; - - std::vector EnvironmentMapDescriptors; - - std::ifstream file(SETTINGS_FILE_NAME); - if (file.is_open()) - { - std::string line; - bool bReadingSection = false; - bool bRecentlyReadEmptyLine = false; - bool bCurrentlyReadingEnvMap = false; - FEnvironmentMapDescriptor desc = {}; - while (std::getline(file, line)) - { - if (line[0] == ';') continue; // skip comment lines - if (line == "") { bRecentlyReadEmptyLine = true; continue; } // skip empty lines - - const std::pair SettingNameValuePair = ParseLineINI(line, &bReadingSection); - const std::string& SettingName = SettingNameValuePair.first; - const std::string& SettingValue = SettingNameValuePair.second; - - // Header sections; - if (bReadingSection) - { - bCurrentlyReadingEnvMap = true; - if (bRecentlyReadEmptyLine) - { - EnvironmentMapDescriptors.push_back(desc); - desc = {}; - bRecentlyReadEmptyLine = false; - } - desc.Name = SettingName; - continue; - } - - if (SettingName == "Path") - { - desc.FilePath = SettingValue; - } - if (SettingName == "MaxCLL") - { - desc.MaxContentLightLevel = StrUtil::ParseFloat(SettingValue); - } - bRecentlyReadEmptyLine = false; - } - if (bCurrentlyReadingEnvMap) - { - EnvironmentMapDescriptors.push_back(desc); - desc = {}; - bCurrentlyReadingEnvMap = false; - } - } - else - { - Log::Error("Cannot find settings file %s in current directory: %s", SETTINGS_FILE_NAME, DirectoryUtil::GetCurrentPath().c_str()); - } - - return EnvironmentMapDescriptors; -} - -std::vector VQEngine::ParseHDRProfilesFile() -{ - constexpr char* SETTINGS_FILE_NAME = "Data/HDRDisplayProfiles.ini"; - - std::vector HDRProfiles; - - std::ifstream file(SETTINGS_FILE_NAME); - if (file.is_open()) - { - std::string line; - bool bReadingSection = false; // Section is an .ini term - bool bRecentlyReadEmptyLine = false; - bool bCurrentlyReadingProfile = false; - FDisplayHDRProfile profile = {}; - while (std::getline(file, line)) - { - if (line[0] == ';') continue; // skip comment lines - if (line == "") { bRecentlyReadEmptyLine = true; continue; } // skip empty lines - - const std::pair SettingNameValuePair = ParseLineINI(line, &bReadingSection); - const std::string& SettingName = SettingNameValuePair.first; - const std::string& SettingValue = SettingNameValuePair.second; - - // Header sections; - if (bReadingSection) - { - bCurrentlyReadingProfile = true; - if (bRecentlyReadEmptyLine) - { - HDRProfiles.push_back(profile); - profile = {}; - bRecentlyReadEmptyLine = false; - } - profile.DisplayName = SettingName; - continue; - } - - if (SettingName == "MaxBrightness") - { - profile.MaxBrightness = StrUtil::ParseFloat(SettingValue); - } - if (SettingName == "MinBrightness") - { - profile.MinBrightness = StrUtil::ParseFloat(SettingValue); - } - bRecentlyReadEmptyLine = false; - } - if (bCurrentlyReadingProfile) // Take into account the last item we're reading (.push_back() isn't called above for the last item) - { - HDRProfiles.push_back(profile); - profile = {}; - bCurrentlyReadingProfile = false; - } - } - else - { - Log::Error("Cannot find settings file %s in current directory: %s", SETTINGS_FILE_NAME, DirectoryUtil::GetCurrentPath().c_str()); - } - return HDRProfiles; -} - -#include "Libs/VQUtils/Libs/tinyxml2/tinyxml2.h" -std::vector< FSceneRepresentation> VQEngine::ParseScenesFile() -{ - using namespace tinyxml2; - constexpr char* XML_TAG_SCENE = "scene"; - constexpr char* XML_TAG_ENVIRONMENT_MAP = "environment_map"; - constexpr char* XML_TAG_ENVIRONMENT_MAP_PRESET = "preset"; - //----------------------------------------------------------------- - constexpr char* SCENE_FILES_DIRECTORY = "Data/Levels/"; - //----------------------------------------------------------------- - const std::vector< std::string> SceneFiles = DirectoryUtil::ListFilesInDirectory(SCENE_FILES_DIRECTORY, ".xml"); - std::vector< FSceneRepresentation> SceneRepresentations; - //----------------------------------------------------------------- - - for (const std::string SceneFile : SceneFiles) - { - // scene rep - FSceneRepresentation SceneRep = {}; - SceneRep.SceneName = DirectoryUtil::GetFileNameWithoutExtension(SceneFile); - - // parse XML - tinyxml2::XMLDocument doc; - doc.LoadFile(SceneFile.c_str()); - - XMLElement* pScene = doc.FirstChildElement(XML_TAG_SCENE); - if (pScene) - { - XMLElement* pEnvMap = pScene->FirstChildElement(XML_TAG_ENVIRONMENT_MAP); - if (pEnvMap) - { - XMLElement* pPreset = pEnvMap->FirstChildElement(XML_TAG_ENVIRONMENT_MAP_PRESET); - if (pPreset) - { - XMLNode* pPresetVal = pPreset->FirstChild(); - if (pPresetVal) - { - SceneRep.EnvironmentMapPreset = pPresetVal->Value(); - } - } - } - } - - // TODO: error reporting? - - SceneRepresentations.push_back(SceneRep); - } - - - return SceneRepresentations; -} diff --git a/Source/Application/VQEngine_Render.cpp b/Source/Application/VQEngine_Render.cpp index fccd8d5d..946f0849 100644 --- a/Source/Application/VQEngine_Render.cpp +++ b/Source/Application/VQEngine_Render.cpp @@ -18,11 +18,11 @@ #include "VQEngine.h" #include "Geometry.h" +#include "GPUMarker.h" #include #include - // ------------------------------------------------------------------------------------------------------------------------------------------------------------ // // MAIN @@ -72,9 +72,6 @@ void VQEngine::RenderThread_Main() } } - // busy wait until all loading is finished before calling Exit - while (mWorkers_Load.GetNumActiveTasks() > 0); - RenderThread_Exit(); Log::Info("RenderThread_Main() : Exit"); } @@ -177,18 +174,21 @@ void VQEngine::RenderThread_Exit() void VQEngine::InitializeBuiltinMeshes() { + using VertexType = FVertexWithNormalAndTangent; { - GeometryGenerator::GeometryData data = GeometryGenerator::Triangle(1.0f); - mBuiltinMeshNames[EBuiltInMeshes::TRIANGLE] = "Triangle"; - mBuiltinMeshes[EBuiltInMeshes::TRIANGLE] = Mesh(&mRenderer, data.Vertices, data.Indices, mBuiltinMeshNames[EBuiltInMeshes::TRIANGLE]); + GeometryGenerator::GeometryData data = GeometryGenerator::Triangle(1.0f); + mResourceNames.mBuiltinMeshNames[EBuiltInMeshes::TRIANGLE] = "Triangle"; + mBuiltinMeshes[EBuiltInMeshes::TRIANGLE] = Mesh(&mRenderer, data.Vertices, data.Indices, mResourceNames.mBuiltinMeshNames[EBuiltInMeshes::TRIANGLE]); } { - GeometryGenerator::GeometryData data = GeometryGenerator::Cube(); - mBuiltinMeshNames[EBuiltInMeshes::CUBE] = "Cube"; - mBuiltinMeshes[EBuiltInMeshes::CUBE] = Mesh(&mRenderer, data.Vertices, data.Indices, mBuiltinMeshNames[EBuiltInMeshes::CUBE]); + GeometryGenerator::GeometryData data = GeometryGenerator::Cube(); + mResourceNames.mBuiltinMeshNames[EBuiltInMeshes::CUBE] = "Cube"; + mBuiltinMeshes[EBuiltInMeshes::CUBE] = Mesh(&mRenderer, data.Vertices, data.Indices, mResourceNames.mBuiltinMeshNames[EBuiltInMeshes::CUBE]); } // ... + + mRenderer.UploadVertexAndIndexBufferHeaps(); } @@ -342,7 +342,7 @@ void VQEngine::RenderThread_RenderMainWindow() HRESULT hr = S_OK; FWindowRenderContext& ctx = mRenderer.GetWindowRenderContext(mpWinMain->GetHWND()); - hr = mbLoadingLevel + hr = mbLoadingLevel || mbLoadingEnvironmentMap ? RenderThread_RenderMainWindow_LoadingScreen(ctx) : RenderThread_RenderMainWindow_Scene(ctx); @@ -360,6 +360,8 @@ void VQEngine::RenderThread_RenderMainWindow() void VQEngine::RenderThread_RenderDebugWindow() { + // temporarily disabled +#if 0 if (mScene_DebugWnd.mFrameData.empty()) return; @@ -457,6 +459,7 @@ void VQEngine::RenderThread_RenderDebugWindow() hr = ctx.SwapChain.Present(); ctx.SwapChain.MoveToNextFrame(); //return hr; +#endif } HRESULT VQEngine::RenderThread_RenderMainWindow_LoadingScreen(FWindowRenderContext& ctx) @@ -465,8 +468,6 @@ HRESULT VQEngine::RenderThread_RenderMainWindow_LoadingScreen(FWindowRenderConte const int NUM_BACK_BUFFERS = ctx.SwapChain.GetNumBackBuffers(); const int BACK_BUFFER_INDEX = ctx.SwapChain.GetCurrentBackBufferIndex(); const int FRAME_DATA_INDEX = mNumRenderLoopsExecuted % NUM_BACK_BUFFERS; - assert(mScene_MainWnd.mLoadingScreenData.size() > 0); - const FLoadingScreenData& FrameData = mScene_MainWnd.mLoadingScreenData[FRAME_DATA_INDEX]; assert(ctx.mCommandAllocatorsGFX.size() >= NUM_BACK_BUFFERS); const bool bUseHDRRenderPath = this->ShouldRenderHDR(mpWinMain->GetHWND()); // ---------------------------------------------------------------------------- @@ -502,10 +503,10 @@ HRESULT VQEngine::RenderThread_RenderMainWindow_LoadingScreen(FWindowRenderConte CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle = ctx.SwapChain.GetCurrentBackBufferRTVHandle(); const float clearColor[] = { - FrameData.SwapChainClearColor[0], - FrameData.SwapChainClearColor[1], - FrameData.SwapChainClearColor[2], - FrameData.SwapChainClearColor[3] + mLoadingScreenData.SwapChainClearColor[0], + mLoadingScreenData.SwapChainClearColor[1], + mLoadingScreenData.SwapChainClearColor[2], + mLoadingScreenData.SwapChainClearColor[3] }; pCmd->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); @@ -527,7 +528,7 @@ HRESULT VQEngine::RenderThread_RenderMainWindow_LoadingScreen(FWindowRenderConte pCmd->SetGraphicsRootSignature(mRenderer.GetRootSignature(1)); pCmd->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps); - pCmd->SetGraphicsRootDescriptorTable(0, mRenderer.GetShaderResourceView(FrameData.SRVLoadingScreen).GetGPUDescHandle()); + pCmd->SetGraphicsRootDescriptorTable(0, mRenderer.GetShaderResourceView(mLoadingScreenData.GetSelectedLoadingScreenSRV_ID()).GetGPUDescHandle()); pCmd->RSSetViewports(1, &viewport); pCmd->RSSetScissorRects(1, &scissorsRect); @@ -563,7 +564,6 @@ HRESULT VQEngine::RenderThread_RenderMainWindow_Scene(FWindowRenderContext& ctx) const int NUM_BACK_BUFFERS = ctx.SwapChain.GetNumBackBuffers(); const int BACK_BUFFER_INDEX = ctx.SwapChain.GetCurrentBackBufferIndex(); const int FRAME_DATA_INDEX = mNumRenderLoopsExecuted % NUM_BACK_BUFFERS; - const FFrameData& FrameData = mScene_MainWnd.mFrameData[FRAME_DATA_INDEX]; assert(ctx.mCommandAllocatorsGFX.size() >= NUM_BACK_BUFFERS); const bool bUseHDRRenderPath = this->ShouldRenderHDR(mpWinMain->GetHWND()); // ---------------------------------------------------------------------------- @@ -597,13 +597,13 @@ HRESULT VQEngine::RenderThread_RenderMainWindow_Scene(FWindowRenderContext& ctx) RenderShadowMaps(ctx); - RenderSceneColor(ctx, FrameData); + RenderSceneColor(ctx, mpScene->GetSceneView(FRAME_DATA_INDEX)); ResolveMSAA(ctx); TransitionForPostProcessing(ctx); - RenderPostProcess(ctx, FrameData.PPParams); + RenderPostProcess(ctx, mpScene->GetPostProcessParameters(FRAME_DATA_INDEX)); RenderUI(ctx); @@ -643,7 +643,6 @@ void VQEngine::DrawMesh(ID3D12GraphicsCommandList* pCmd, const Mesh& mesh) pCmd->DrawIndexedInstanced(NumIndices, NumInstances, 0, 0, 0); } - void VQEngine::TransitionForSceneRendering(FWindowRenderContext& ctx) { const bool& bMSAA = mSettings.gfx.bAntiAliasing; @@ -653,6 +652,8 @@ void VQEngine::TransitionForSceneRendering(FWindowRenderContext& ctx) auto pRscColor = mRenderer.GetTextureResource(mResources_MainWnd.Tex_MainViewColor); auto pRscColorMSAA = mRenderer.GetTextureResource(mResources_MainWnd.Tex_MainViewColorMSAA); + SCOPED_GPU_MARKER(pCmd, "TransitionForSceneRendering"); + if (bMSAA) { const CD3DX12_RESOURCE_BARRIER pBarriers[] = @@ -673,17 +674,25 @@ void VQEngine::TransitionForSceneRendering(FWindowRenderContext& ctx) void VQEngine::RenderShadowMaps(FWindowRenderContext& ctx) { - + ID3D12GraphicsCommandList*& pCmd = ctx.pCmdList_GFX; + //PIXBeginEvent(pCmd, VQ_PIXEventColor, "TransitionForSceneRendering"); + ////TODO + //PIXEndEvent(pCmd); } -void VQEngine::RenderSceneColor(FWindowRenderContext& ctx, const FFrameData& FrameData) +void VQEngine::RenderSceneColor(FWindowRenderContext& ctx, const FSceneView& SceneView) { + using namespace DirectX; + const bool& bMSAA = mSettings.gfx.bAntiAliasing; ID3D12GraphicsCommandList*& pCmd = ctx.pCmdList_GFX; const RTV& rtv = mRenderer.GetRTV(bMSAA ? mResources_MainWnd.RTV_MainViewColorMSAA : mResources_MainWnd.RTV_MainViewColor); const DSV& dsv = mRenderer.GetDSV(bMSAA ? mResources_MainWnd.DSV_MainViewDepthMSAA : mResources_MainWnd.DSV_MainViewDepth); + + SCOPED_GPU_MARKER(pCmd, "RenderSceneColor"); + // Clear Depth & Render targets D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtv.GetCPUDescHandle(); D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = dsv.GetCPUDescHandle(); @@ -692,6 +701,8 @@ void VQEngine::RenderSceneColor(FWindowRenderContext& ctx, const FFrameData& Fra pCmd->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); pCmd->ClearDepthStencilView(dsvHandle, DSVClearFlags, 1.0f, 0, 0, nullptr); + pCmd->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle); + // Set Viewport & Scissors const float RenderResolutionX = static_cast(ctx.MainRTResolutionX); @@ -701,65 +712,77 @@ void VQEngine::RenderSceneColor(FWindowRenderContext& ctx, const FFrameData& Fra pCmd->RSSetViewports(1, &viewport); pCmd->RSSetScissorRects(1, &scissorsRect); - - - // Draw Object ----------------------------------------------- - using namespace DirectX; - const XMMATRIX mMVP - = FrameData.TFCube.WorldTransformationMatrix() - * FrameData.SceneCamera.GetViewMatrix() - * FrameData.SceneCamera.GetProjectionMatrix(); + pCmd->SetPipelineState(mRenderer.GetPSO(bMSAA ? EBuiltinPSOs::OBJECT_PSO_MSAA_4 : EBuiltinPSOs::OBJECT_PSO)); ID3D12DescriptorHeap* ppHeaps[] = { mRenderer.GetDescHeap(EResourceHeapType::CBV_SRV_UAV_HEAP) }; - // set constant buffer data - FrameConstantBuffer* pConstBuffer = {}; - D3D12_GPU_VIRTUAL_ADDRESS cbAddr = {}; - ctx.mDynamicHeap_ConstantBuffer.AllocConstantBuffer(sizeof(FrameConstantBuffer), (void**)(&pConstBuffer), &cbAddr); - pConstBuffer->matModelViewProj = mMVP; - - - pCmd->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle); - - pCmd->SetPipelineState(mRenderer.GetPSO(bMSAA ? EBuiltinPSOs::HELLO_WORLD_CUBE_PSO_MSAA_4 : EBuiltinPSOs::HELLO_WORLD_CUBE_PSO)); - - // hardcoded root signature for now until shader reflection and rootsignature management is implemented - pCmd->SetGraphicsRootSignature(mRenderer.GetRootSignature(2)); - - pCmd->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps); - pCmd->SetGraphicsRootDescriptorTable(0, mRenderer.GetSRV(0).GetGPUDescHandle()); - pCmd->SetGraphicsRootConstantBufferView(1, cbAddr); - -#if 0 - DrawMesh(pCmd, mBuiltinMeshes[EBuiltInMeshes::CUBE]); -#else - const Mesh& mesh = mBuiltinMeshes[EBuiltInMeshes::CUBE]; - const auto VBIBIDs = mesh.GetIABufferIDs(); - const uint32 NumIndices = mesh.GetNumIndices(); - const uint32 NumInstances = 1; - const BufferID& VB_ID = VBIBIDs.first; - const BufferID& IB_ID = VBIBIDs.second; - const VBV& vb = mRenderer.GetVertexBufferView(VB_ID); - const IBV& ib = mRenderer.GetIndexBufferView(IB_ID); - - pCmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - pCmd->IASetVertexBuffers(0, 1, &vb); - pCmd->IASetIndexBuffer(&ib); - - pCmd->DrawIndexedInstanced(NumIndices, NumInstances, 0, 0, 0); -#endif + // Draw Objects ----------------------------------------------- + { + SCOPED_GPU_MARKER(pCmd, "Geometry"); + pCmd->SetGraphicsRootSignature(mRenderer.GetRootSignature(4)); // hardcoded root signature for now until shader reflection and rootsignature management is implemented + pCmd->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps); + for (const FMeshRenderCommand& meshRenderCmd : SceneView.meshRenderCommands) + { + // set constant buffer data + const XMMATRIX mMVP + = meshRenderCmd.WorldTransformationMatrix + * SceneView.viewProj; + + FFrameConstantBuffer2* pConstBuffer = {}; + D3D12_GPU_VIRTUAL_ADDRESS cbAddr = {}; + ctx.mDynamicHeap_ConstantBuffer.AllocConstantBuffer(sizeof(FFrameConstantBuffer2), (void**)(&pConstBuffer), &cbAddr); + pConstBuffer->matModelViewProj = mMVP; + pConstBuffer->iTextureConfig = 0; // TODO + pConstBuffer->iTextureOutput = EMaterialTextureMapBindings::ALBEDO; // TODO: drive thru material + + // set material data + const Material& mat = mpScene->GetMaterial(meshRenderCmd.matID); + if (mat.SRVMaterialMaps != INVALID_ID) + pCmd->SetGraphicsRootDescriptorTable(0, mRenderer.GetSRV(mat.SRVMaterialMaps).GetGPUDescHandle(0)); + + pCmd->SetGraphicsRootConstantBufferView(1, cbAddr); + + const Mesh& mesh = mpScene->mMeshes.at(meshRenderCmd.meshID); + + const auto VBIBIDs = mesh.GetIABufferIDs(); + const uint32 NumIndices = mesh.GetNumIndices(); + const uint32 NumInstances = 1; + const BufferID& VB_ID = VBIBIDs.first; + const BufferID& IB_ID = VBIBIDs.second; + const VBV& vb = mRenderer.GetVertexBufferView(VB_ID); + const IBV& ib = mRenderer.GetIndexBufferView(IB_ID); + + pCmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + pCmd->IASetVertexBuffers(0, 1, &vb); + pCmd->IASetIndexBuffer(&ib); + + pCmd->DrawIndexedInstanced(NumIndices, NumInstances, 0, 0, 0); + } + } // Draw Environment Map --------------------------------------- const bool bHasEnvironmentMapHDRTexture = mResources_MainWnd.EnvironmentMap.SRV_HDREnvironment != INVALID_ID; const bool bDrawEnvironmentMap = bHasEnvironmentMapHDRTexture && true; if (bDrawEnvironmentMap) { - Camera skyCam = FrameData.SceneCamera; - skyCam.SetPosition(0, 0, 0); - skyCam.UpdateViewMatrix(); - - cbAddr = {}; - ctx.mDynamicHeap_ConstantBuffer.AllocConstantBuffer(sizeof(FrameConstantBuffer), (void**)(&pConstBuffer), &cbAddr); + SCOPED_GPU_MARKER(pCmd, "EnvironmentMap"); + + ID3D12DescriptorHeap* ppHeaps[] = { mRenderer.GetDescHeap(EResourceHeapType::CBV_SRV_UAV_HEAP) }; + + Camera skyCam = mpScene->GetActiveCamera().Clone(); + FCameraParameters p = {}; + p.bInitializeCameraController = false; + p.ProjectionParams = skyCam.GetProjectionParameters(); + p.ProjectionParams.bPerspectiveProjection = true; + p.ProjectionParams.FieldOfView = p.ProjectionParams.FieldOfView * RAD2DEG; // TODO: remove the need for this conversion + p.x = p.y = p.z = 0; + p.Yaw = SceneView.MainViewCameraYaw * RAD2DEG; + p.Pitch = SceneView.MainViewCameraPitch * RAD2DEG; + skyCam.InitializeCamera(p); + + D3D12_GPU_VIRTUAL_ADDRESS cbAddr = {}; + FFrameConstantBuffer * pConstBuffer = {}; + ctx.mDynamicHeap_ConstantBuffer.AllocConstantBuffer(sizeof(FFrameConstantBuffer ), (void**)(&pConstBuffer), &cbAddr); pConstBuffer->matModelViewProj = skyCam.GetViewMatrix() * skyCam.GetProjectionMatrix(); pCmd->SetPipelineState(mRenderer.GetPSO(bMSAA ? EBuiltinPSOs::SKYDOME_PSO_MSAA_4 : EBuiltinPSOs::SKYDOME_PSO)); @@ -771,11 +794,7 @@ void VQEngine::RenderSceneColor(FWindowRenderContext& ctx, const FFrameData& Fra pCmd->SetGraphicsRootDescriptorTable(0, mRenderer.GetSRV(mResources_MainWnd.EnvironmentMap.SRV_HDREnvironment).GetGPUDescHandle()); pCmd->SetGraphicsRootConstantBufferView(1, cbAddr); - pCmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - pCmd->IASetVertexBuffers(0, 1, &vb); - pCmd->IASetIndexBuffer(&ib); - - pCmd->DrawIndexedInstanced(NumIndices, NumInstances, 0, 0, 0); + DrawMesh(pCmd, mBuiltinMeshes[EBuiltInMeshes::CUBE]); } } @@ -787,7 +806,7 @@ void VQEngine::ResolveMSAA(FWindowRenderContext& ctx) if (!bMSAA) return; - + SCOPED_GPU_MARKER(pCmd, "ResolveMSAA"); auto pRscColor = mRenderer.GetTextureResource(mResources_MainWnd.Tex_MainViewColor); auto pRscColorMSAA = mRenderer.GetTextureResource(mResources_MainWnd.Tex_MainViewColorMSAA); @@ -812,6 +831,7 @@ void VQEngine::TransitionForPostProcessing(FWindowRenderContext& ctx) auto pRscInput = mRenderer.GetTextureResource(mResources_MainWnd.Tex_MainViewColor); auto pRscOutput = mRenderer.GetTextureResource(mResources_MainWnd.Tex_PostProcess_TonemapperOut); + SCOPED_GPU_MARKER(pCmd, "TransitionForPostProcessing"); const CD3DX12_RESOURCE_BARRIER pBarriers[] = { CD3DX12_RESOURCE_BARRIER::Transition(pRscInput , (bMSAA ? D3D12_RESOURCE_STATE_RESOLVE_DEST : D3D12_RESOURCE_STATE_RENDER_TARGET), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE) @@ -844,6 +864,7 @@ void VQEngine::RenderPostProcess(FWindowRenderContext& ctx, const FPostProcessPa constexpr int DispatchZ = 1; // cmds + SCOPED_GPU_MARKER(pCmd, "RenderPostProcess"); pCmd->SetPipelineState(mRenderer.GetPSO(EBuiltinPSOs::TONEMAPPER_PSO)); pCmd->SetComputeRootSignature(mRenderer.GetRootSignature(3)); // compute RS pCmd->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps); @@ -872,6 +893,7 @@ void VQEngine::RenderUI(FWindowRenderContext& ctx) ID3D12Resource* pSwapChainRT = ctx.SwapChain.GetCurrentBackBufferRenderTarget(); D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = ctx.SwapChain.GetCurrentBackBufferRTVHandle(); + SCOPED_GPU_MARKER(pCmd, "SwapchainPassthrough"); // Transition Input & Output resources auto pRscTonemapperOut = mRenderer.GetTextureResource(mResources_MainWnd.Tex_PostProcess_TonemapperOut); CD3DX12_RESOURCE_BARRIER barriers[] = @@ -907,12 +929,14 @@ HRESULT VQEngine::PresentFrame(FWindowRenderContext& ctx) ID3D12GraphicsCommandList*& pCmd = ctx.pCmdList_GFX; ID3D12Resource* pSwapChainRT = ctx.SwapChain.GetCurrentBackBufferRenderTarget(); - // Transition SwapChain for Present - pCmd->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pSwapChainRT - , D3D12_RESOURCE_STATE_RENDER_TARGET - , D3D12_RESOURCE_STATE_PRESENT) - ); - + { + SCOPED_GPU_MARKER(pCmd, "PresentFrame"); + // Transition SwapChain for Present + pCmd->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pSwapChainRT + , D3D12_RESOURCE_STATE_RENDER_TARGET + , D3D12_RESOURCE_STATE_PRESENT) + ); + } pCmd->Close(); ID3D12CommandList* ppCommandLists[] = { pCmd }; diff --git a/Source/Application/VQEngine_Update.cpp b/Source/Application/VQEngine_Update.cpp index b730d5c1..1c9733cf 100644 --- a/Source/Application/VQEngine_Update.cpp +++ b/Source/Application/VQEngine_Update.cpp @@ -20,6 +20,9 @@ #include "VQEngine.h" #include "Math.h" +#include "Scene.h" +#include "../Scenes/Scenes.h" + #include "Libs/VQUtils/Source/utils.h" @@ -41,6 +44,8 @@ void VQEngine::UpdateThread_Main() float dt = 0.0f; while (!mbStopAllThreads && !bQuit) { + UpdateThread_WaitForRenderThread(); + UpdateThread_HandleEvents(); UpdateThread_PreUpdate(dt); @@ -56,8 +61,6 @@ void VQEngine::UpdateThread_Main() ++mNumUpdateLoopsExecuted; UpdateThread_SignalRenderThread(); - - UpdateThread_WaitForRenderThread(); } UpdateThread_Exit(); @@ -66,17 +69,8 @@ void VQEngine::UpdateThread_Main() void VQEngine::UpdateThread_Inititalize() { - mActiveEnvironmentMapPresetIndex = -1; mNumUpdateLoopsExecuted.store(0); - -#if ENABLE_RAW_INPUT - // initialize raw input - Input::InitRawInputDevices(mpWinMain->GetHWND()); -#endif - - // initialize input states - RegisterWindowForInput(mpWinMain); - RegisterWindowForInput(mpWinDebug); + mbLoadingEnvironmentMap.store(false); // busy lock until render thread is initialized while (!mbRenderThreadInitialized); @@ -90,6 +84,7 @@ void VQEngine::UpdateThread_Inititalize() void VQEngine::UpdateThread_Exit() { + mpScene->Unload(); } void VQEngine::UpdateThread_PreUpdate(float& dt) @@ -111,29 +106,47 @@ void VQEngine::UpdateThread_UpdateAppState(const float dt) if (mAppState == EAppState::INITIALIZING) { // start loading - Log::Info("Update Thread starts loading..."); + Log::Info("UpdateThread: loading..."); // start load level Load_SceneData_Dispatch(); - mAppState = EAppState::LOADING;// not thread-safe - mbLoadingLevel.store(true); // thread-safe + // set state + mAppState = EAppState::LOADING;// not thread-safe + } - if (mbLoadingLevel) + if (mbLoadingLevel || mbLoadingEnvironmentMap) { // animate loading screen // check if loading is done - const int NumActiveTasks = mWorkers_Load.GetNumActiveTasks(); + const int NumActiveTasks = mWorkers_ModelLoading.GetNumActiveTasks() + mWorkers_TextureLoading.GetNumActiveTasks(); const bool bLoadDone = NumActiveTasks == 0; if (bLoadDone) { - Log::Info("Update Thread loaded, starting simulation..."); + if (mbLoadingLevel) + { + mpScene->OnLoadComplete(); + } + // OnEnvMapLoaded = noop + + WaitUntilRenderingFinishes(); + Log::Info("Loading completed, starting scene simulation"); mAppState = EAppState::SIMULATING; - mbLoadingLevel.store(false); + + if (mbLoadingLevel) + { + mbLoadingLevel.store(false); + } + if (mbLoadingEnvironmentMap) + { + mbLoadingEnvironmentMap.store(false); + } + + mLoadingScreenData.RotateLoadingScreenImage(); } } @@ -158,12 +171,7 @@ void VQEngine::UpdateThread_PostUpdate() return; } - // compute visibility - - // extract scene view - - // copy over state for next frame - mScene_MainWnd.mFrameData[FRAME_DATA_NEXT_INDEX] = mScene_MainWnd.mFrameData[FRAME_DATA_INDEX]; + mpScene->PostUpdate(FRAME_DATA_INDEX, FRAME_DATA_NEXT_INDEX); // input post update for (auto it = mInputStates.begin(); it != mInputStates.end(); ++it) @@ -192,31 +200,17 @@ void VQEngine::UpdateThread_SignalRenderThread() // ------------------------------------------------------------------- -// temporary hardcoded initialization until scene is data driven -static FCameraData GenerateCameraInitializationParameters(const std::unique_ptr& pWin) -{ - assert(pWin); - FCameraData camData = {}; - camData.x = 0.0f; camData.y = 3.0f; camData.z = -5.0f; - camData.pitch = 15.0f; - camData.yaw = 0.0f; - camData.bPerspectiveProjection = true; - camData.fovV_Degrees = 60.0f; - camData.nearPlane = 0.01f; - camData.farPlane = 1000.0f; - camData.width = static_cast(pWin->GetWidth()); - camData.height = static_cast(pWin->GetHeight()); - return camData; -} -static void Toggle(bool& b) { b = !b; } - void VQEngine::HandleEngineInput() { + const int NUM_BACK_BUFFERS = mRenderer.GetSwapChainBackBufferCount(mpWinMain->GetHWND()); + const int FRAME_DATA_INDEX = mNumUpdateLoopsExecuted % NUM_BACK_BUFFERS; + for (decltype(mInputStates)::iterator it = mInputStates.begin(); it != mInputStates.end(); ++it) { HWND hwnd = it->first; Input& input = it->second; auto& pWin = this->GetWindow(hwnd); + const bool bIsShiftDown = input.IsKeyDown("Shift"); // // Process-level input handling @@ -240,34 +234,38 @@ void VQEngine::HandleEngineInput() mEventQueue_VQEToWin_Main.AddItem(std::make_shared(hwnd, true, false)); } } - - // - // Graphics Settings Controls - // - if (input.IsKeyTriggered("V")) + if (pWin == mpWinMain) { - if (pWin == mpWinMain) + // Graphics Settings Controls + if (input.IsKeyTriggered("V")) { auto& SwapChain = mRenderer.GetWindowSwapChain(hwnd); mEventQueue_WinToVQE_Renderer.AddItem(std::make_shared(hwnd, !SwapChain.IsVSyncOn())); } - } - if (input.IsKeyTriggered("M")) - { - if (pWin == mpWinMain) + if (input.IsKeyTriggered("M")) { mSettings.gfx.bAntiAliasing = !mSettings.gfx.bAntiAliasing; - Log::Info("Toggle MSAA: %d", mSettings.gfx.bAntiAliasing); + Log::Info("Toggle MSAA: %d", mSettings.gfx.bAntiAliasing); } - } - if (input.IsKeyTriggered("G")) - { - if (pWin == mpWinMain) + if (input.IsKeyTriggered("G")) { - FFrameData& data = GetCurrentFrameData(mpWinMain->GetHWND()); - data.PPParams.ToggleGammaCorrection = data.PPParams.ToggleGammaCorrection == 1 ? 0 : 1; - Log::Info("Tonemapper: ApplyGamma=%d (SDR-only)", data.PPParams.ToggleGammaCorrection); + FPostProcessParameters& PPParams = mpScene->GetPostProcessParameters(FRAME_DATA_INDEX); + PPParams.ToggleGammaCorrection = PPParams.ToggleGammaCorrection == 1 ? 0 : 1; + Log::Info("Tonemapper: ApplyGamma=%d (SDR-only)", PPParams.ToggleGammaCorrection); } + + // Scene switching + if (bIsShiftDown) + { + const int NumScenes = static_cast(mResourceNames.mSceneNames.size()); + if (input.IsKeyTriggered("PageUp") && !mbLoadingLevel) { mIndex_SelectedScene = CircularIncrement(mIndex_SelectedScene, NumScenes); this->StartLoadingScene(mIndex_SelectedScene); } + if (input.IsKeyTriggered("PageDown") && !mbLoadingLevel){ mIndex_SelectedScene = CircularDecrement(mIndex_SelectedScene, NumScenes - 1); this->StartLoadingScene(mIndex_SelectedScene); } + if (input.IsKeyTriggered("R") && !mbLoadingLevel) { this->StartLoadingScene(mIndex_SelectedScene); } // reload scene + } + if (input.IsKeyTriggered("1") && !mbLoadingLevel) { mIndex_SelectedScene = 0; this->StartLoadingScene(mIndex_SelectedScene); } + if (input.IsKeyTriggered("2") && !mbLoadingLevel) { mIndex_SelectedScene = 1; this->StartLoadingScene(mIndex_SelectedScene); } + if (input.IsKeyTriggered("3") && !mbLoadingLevel) { mIndex_SelectedScene = 2; this->StartLoadingScene(mIndex_SelectedScene); } + if (input.IsKeyTriggered("4") && !mbLoadingLevel) { mIndex_SelectedScene = 3; this->StartLoadingScene(mIndex_SelectedScene); } } } } @@ -284,7 +282,7 @@ bool VQEngine::ShouldRenderHDR(HWND hwnd) const return mSettings.WndMain.bEnableHDR && pWin->GetIsOnHDRCapableDisplay(); } -void VQEngine::CalculateEffectiveFrameRate(HWND hwnd) +void VQEngine::CalculateEffectiveFrameRateLimit(HWND hwnd) { if (mSettings.gfx.MaxFrameRate == -1) { @@ -332,6 +330,8 @@ FSetHDRMetaDataParams VQEngine::GatherHDRMetaDataParameters(HWND hwnd) { FSetHDRMetaDataParams params; + while (!mbRenderThreadInitialized); // wait until renderer is initialized + const SwapChain& Swapchain = mRenderer.GetWindowSwapChain(hwnd); const DXGI_OUTPUT_DESC1 desc = Swapchain.GetContainingMonitorDesc(); const FDisplayHDRProfile* pProfile = GetHDRProfileIfExists(desc.DeviceName); @@ -366,12 +366,63 @@ const std::string& VQEngine::GetWindowName(HWND hwnd) const return mWinNameLookup.at(hwnd); } -FFrameData& VQEngine::GetCurrentFrameData(HWND hwnd) + +MeshID VQEngine::GetBuiltInMeshID(const std::string& MeshName) const { - const int NUM_BACK_BUFFERS = mRenderer.GetSwapChainBackBufferCount(hwnd); - const int FRAME_DATA_INDEX = mNumUpdateLoopsExecuted % NUM_BACK_BUFFERS; - return mWindowUpdateContextLookup.at(hwnd)->mFrameData[FRAME_DATA_INDEX]; + const auto it = std::find(mResourceNames.mBuiltinMeshNames.begin(), mResourceNames.mBuiltinMeshNames.end(), MeshName); + if (it == mResourceNames.mBuiltinMeshNames.end()) + { + Log::Error("Builtin Mesh Not Found: %s", MeshName.c_str()); + return INVALID_ID; + } + return static_cast(it - mResourceNames.mBuiltinMeshNames.begin()); +} + +void VQEngine::StartLoadingEnvironmentMap(int IndexEnvMap) +{ + this->WaitUntilRenderingFinishes(); + mAppState = EAppState::LOADING; + mbLoadingEnvironmentMap = true; + mWorkers_TextureLoading.AddTask([&, IndexEnvMap]() + { + LoadEnvironmentMap(mResourceNames.mEnvironmentMapPresetNames[IndexEnvMap]); + }); +} + +void VQEngine::StartLoadingScene(int IndexScene) +{ + assert(IndexScene >= 0 && IndexScene < mResourceNames.mSceneNames.size()); + + // get scene representation + const std::string& SceneName = mResourceNames.mSceneNames[IndexScene]; + + + + // queue the selected scene for loading + mQueue_SceneLoad.push(mResourceNames.mSceneNames[IndexScene]); + + mAppState = INITIALIZING; + mbLoadingLevel.store(true); // thread-safe + Log::Info("StartLoadingScene: %d", IndexScene); +} + +void VQEngine::UnloadEnvironmentMap() +{ + FEnvironmentMap& env = mResources_MainWnd.EnvironmentMap; + if (env.Tex_HDREnvironment != INVALID_ID) + { + // GPU-sync assumed + mRenderer.GetWindowSwapChain(mpWinMain->GetHWND()).WaitForGPU(); + mRenderer.DestroySRV(env.SRV_HDREnvironment); + mRenderer.DestroyTexture(env.Tex_HDREnvironment); + env.SRV_HDREnvironment = env.Tex_HDREnvironment = INVALID_ID; + env.MaxContentLightLevel = 0; + } +} +void VQEngine::WaitUntilRenderingFinishes() +{ + while (mNumRenderLoopsExecuted != mNumUpdateLoopsExecuted); } const FEnvironmentMapDescriptor& VQEngine::GetEnvironmentMapDesc(const std::string& EnvMapName) const @@ -393,86 +444,11 @@ void VQEngine::UpdateThread_UpdateScene_MainWnd(const float dt) { std::unique_ptr& pWin = mpWinMain; HWND hwnd = pWin->GetHWND(); - FFrameData& FrameData = GetCurrentFrameData(hwnd); - const Input& input = mInputStates.at(hwnd); - - // handle input - if (input.IsKeyTriggered('R')) FrameData.SceneCamera.InitializeCamera(GenerateCameraInitializationParameters(mpWinMain)); - - constexpr float CAMERA_MOVEMENT_SPEED_MULTIPLER = 0.75f; - constexpr float CAMERA_MOVEMENT_SPEED_SHIFT_MULTIPLER = 2.0f; - XMVECTOR LocalSpaceTranslation = XMVectorSet(0, 0, 0, 0); - if (input.IsKeyDown('A')) LocalSpaceTranslation += XMLoadFloat3(&LeftVector); - if (input.IsKeyDown('D')) LocalSpaceTranslation += XMLoadFloat3(&RightVector); - if (input.IsKeyDown('W')) LocalSpaceTranslation += XMLoadFloat3(&ForwardVector); - if (input.IsKeyDown('S')) LocalSpaceTranslation += XMLoadFloat3(&BackVector); - if (input.IsKeyDown('E')) LocalSpaceTranslation += XMLoadFloat3(&UpVector); - if (input.IsKeyDown('Q')) LocalSpaceTranslation += XMLoadFloat3(&DownVector); - if (input.IsKeyDown(VK_SHIFT)) LocalSpaceTranslation *= CAMERA_MOVEMENT_SPEED_SHIFT_MULTIPLER; - LocalSpaceTranslation *= CAMERA_MOVEMENT_SPEED_MULTIPLER; - - if (input.IsKeyTriggered("Space")) Toggle(FrameData.bCubeAnimating); - - constexpr float MOUSE_BUTTON_ROTATION_SPEED_MULTIPLIER = 1.0f; - if (input.IsMouseDown(Input::EMouseButtons::MOUSE_BUTTON_LEFT)) FrameData.TFCube.RotateAroundAxisRadians(ZAxis, dt * PI * MOUSE_BUTTON_ROTATION_SPEED_MULTIPLIER); - if (input.IsMouseDown(Input::EMouseButtons::MOUSE_BUTTON_RIGHT)) FrameData.TFCube.RotateAroundAxisRadians(YAxis, dt * PI * MOUSE_BUTTON_ROTATION_SPEED_MULTIPLIER); - if (input.IsMouseDown(Input::EMouseButtons::MOUSE_BUTTON_MIDDLE)) FrameData.TFCube.RotateAroundAxisRadians(XAxis, dt * PI * MOUSE_BUTTON_ROTATION_SPEED_MULTIPLIER); - - constexpr float DOUBLE_CLICK_MULTIPLIER = 4.0f; - if (input.IsMouseDoubleClick(Input::EMouseButtons::MOUSE_BUTTON_LEFT)) FrameData.TFCube.RotateAroundAxisRadians(ZAxis, dt * PI * DOUBLE_CLICK_MULTIPLIER); - if (input.IsMouseDoubleClick(Input::EMouseButtons::MOUSE_BUTTON_RIGHT)) FrameData.TFCube.RotateAroundAxisRadians(YAxis, dt * PI * DOUBLE_CLICK_MULTIPLIER); - if (input.IsMouseDoubleClick(Input::EMouseButtons::MOUSE_BUTTON_MIDDLE)) FrameData.TFCube.RotateAroundAxisRadians(XAxis, dt * PI * DOUBLE_CLICK_MULTIPLIER); - - constexpr float SCROLL_SCALE_DELTA = 1.1f; - const float CubeScale = FrameData.TFCube._scale.x; - if (input.IsMouseScrollUp() ) FrameData.TFCube.SetUniformScale(CubeScale * SCROLL_SCALE_DELTA); - if (input.IsMouseScrollDown()) FrameData.TFCube.SetUniformScale(std::max(0.5f, CubeScale / SCROLL_SCALE_DELTA)); - - - auto fnBusyWaitUntilRenderThreadCatchesUp = [&]() - { - while ((mNumRenderLoopsExecuted+1) != mNumUpdateLoopsExecuted); - }; - - const FEnvironmentMap& env = mResources_MainWnd.EnvironmentMap; - const int NumEnvMaps = static_cast(mEnvironmentMapPresetNames.size()); - if (input.IsKeyTriggered("PageUp")) - { - fnBusyWaitUntilRenderThreadCatchesUp(); - mActiveEnvironmentMapPresetIndex = (mActiveEnvironmentMapPresetIndex + 1) % NumEnvMaps; - mAppState = EAppState::LOADING; - mbLoadingLevel = true; - mWorkers_Load.AddTask([&]() - { - LoadEnvironmentMap(mEnvironmentMapPresetNames[mActiveEnvironmentMapPresetIndex]); - }); - - - } - if (input.IsKeyTriggered("PageDown")) - { - fnBusyWaitUntilRenderThreadCatchesUp(); - mActiveEnvironmentMapPresetIndex = mActiveEnvironmentMapPresetIndex == 0 - ? NumEnvMaps - 1 - : mActiveEnvironmentMapPresetIndex - 1; - mAppState = EAppState::LOADING; - mbLoadingLevel = true; - mWorkers_Load.AddTask([&]() - { - LoadEnvironmentMap(mEnvironmentMapPresetNames[mActiveEnvironmentMapPresetIndex]); - }); - } + const int NUM_BACK_BUFFERS = mRenderer.GetSwapChainBackBufferCount(hwnd); + const int FRAME_DATA_INDEX = mNumUpdateLoopsExecuted % NUM_BACK_BUFFERS; - // update camera - FCameraInput camInput(LocalSpaceTranslation); - camInput.DeltaMouseXY = input.GetMouseDelta(); - FrameData.SceneCamera.Update(dt, camInput); - - // update scene data - if(FrameData.bCubeAnimating) - FrameData.TFCube.RotateAroundAxisRadians(YAxis, dt * 0.2f * PI); - + mpScene->Update(dt, FRAME_DATA_INDEX); } void VQEngine::UpdateThread_UpdateScene_DebugWnd(const float dt) @@ -481,7 +457,6 @@ void VQEngine::UpdateThread_UpdateScene_DebugWnd(const float dt) std::unique_ptr& pWin = mpWinDebug; HWND hwnd = pWin->GetHWND(); - FFrameData& FrameData = GetCurrentFrameData(hwnd); const Input& input = mInputStates.at(hwnd); @@ -493,59 +468,38 @@ void VQEngine::Load_SceneData_Dispatch() if (mQueue_SceneLoad.empty()) return; - const FSceneRepresentation SceneRep = mQueue_SceneLoad.front(); + const std::string SceneFileName = mQueue_SceneLoad.front(); mQueue_SceneLoad.pop(); + - mWorkers_Load.AddTask([&]() // Load scene data + const int NUM_SWAPCHAIN_BACKBUFFERS = mSettings.gfx.bUseTripleBuffering ? 3 : 2; + const Input& input = mInputStates.at(mpWinMain->GetHWND()); + + auto fnCreateSceneInstance = [&](const std::string& SceneType, std::unique_ptr& pScene) -> void { - const int NumBackBuffer_WndMain = mRenderer.GetSwapChainBackBufferCount(mpWinMain); - const int NumBackBuffer_WndDbg = mRenderer.GetSwapChainBackBufferCount(mpWinDebug); + if (SceneType == "Default") pScene = std::make_unique(*this, NUM_SWAPCHAIN_BACKBUFFERS, input, mpWinMain, mRenderer); + else if (SceneType == "Sponza") pScene = std::make_unique(*this, NUM_SWAPCHAIN_BACKBUFFERS, input, mpWinMain, mRenderer); + else if (SceneType == "StressTest") pScene = std::make_unique(*this, NUM_SWAPCHAIN_BACKBUFFERS, input, mpWinMain, mRenderer); + else if (SceneType == "GeometryUnitTest") pScene = std::make_unique(*this, NUM_SWAPCHAIN_BACKBUFFERS, input, mpWinMain, mRenderer); + }; - // TODO: initialize window scene data here for now, should update this to proper location later on (Scene probably?) - FFrameData data[2]; + if (mpScene) + { + this->WaitUntilRenderingFinishes(); + mpScene->Unload(); + } - // - // MAIN WINDOW DATA - // - data[0].SwapChainClearColor = { 0.07f, 0.07f, 0.07f, 1.0f }; - - // Cube Data - constexpr XMFLOAT3 CUBE_POSITION = XMFLOAT3(0, 0, 4); - constexpr float CUBE_SCALE = 3.0f; - constexpr XMFLOAT3 CUBE_ROTATION_VECTOR = XMFLOAT3(1, 1, 1); - constexpr float CUBE_ROTATION_DEGREES = 60.0f; - const XMVECTOR CUBE_ROTATION_AXIS = XMVector3Normalize(XMLoadFloat3(&CUBE_ROTATION_VECTOR)); - data[0].TFCube = Transform( - CUBE_POSITION - , Quaternion::FromAxisAngle(CUBE_ROTATION_AXIS, CUBE_ROTATION_DEGREES * DEG2RAD) - , XMFLOAT3(CUBE_SCALE, CUBE_SCALE, CUBE_SCALE) - ); - data[0].bCubeAnimating = true; - // Camera Data - FCameraData camData = GenerateCameraInitializationParameters(mpWinMain); - data[0].SceneCamera.InitializeCamera(camData); - // Post Process Data - data[0].PPParams.ContentColorSpace = EColorSpace::REC_709; - data[0].PPParams.OutputDisplayCurve = ShouldRenderHDR(mpWinMain->GetHWND()) ? EDisplayCurve::Linear : EDisplayCurve::sRGB; - data[0].PPParams.DisplayReferenceBrightnessLevel = 200.0f; - - mScene_MainWnd.mFrameData.resize(NumBackBuffer_WndMain, data[0]); + const std::string SceneFilePath = "Data/Levels/" + SceneFileName + ".xml"; + FSceneRepresentation SceneRep = VQEngine::ParseSceneFile(SceneFilePath); + fnCreateSceneInstance(SceneRep.SceneName, mpScene); - // - // DEBUG WINDOW DATA - // - data[1].SwapChainClearColor = { 0.20f, 0.21f, 0.21f, 1.0f }; - mScene_DebugWnd.mFrameData.resize(NumBackBuffer_WndDbg, data[1]); + // start loading; + mpScene->StartLoading(this->mBuiltinMeshes, SceneRep); - mWindowUpdateContextLookup[mpWinMain->GetHWND()] = &mScene_MainWnd; - if (mpWinDebug) mWindowUpdateContextLookup[mpWinDebug->GetHWND()] = &mScene_DebugWnd; - }); - mWorkers_Load.AddTask([&, SceneRep]() // Load Environment map textures - { - Log::Info("[Scene] Loading: %s", SceneRep.SceneName.c_str()); - LoadEnvironmentMap(SceneRep.EnvironmentMapPreset); - Log::Info("[Scene] %s loaded.", SceneRep.SceneName.c_str()); - }); + if (!SceneRep.EnvironmentMapPreset.empty()) + { + mWorkers_TextureLoading.AddTask([=]() { LoadEnvironmentMap(SceneRep.EnvironmentMapPreset); }); + } } void VQEngine::LoadEnvironmentMap(const std::string& EnvMapName) @@ -557,16 +511,12 @@ void VQEngine::LoadEnvironmentMap(const std::string& EnvMapName) if (env.Tex_HDREnvironment != INVALID_ID) { assert(env.SRV_HDREnvironment != INVALID_ID); - // GPU-sync assumed - mRenderer.GetWindowSwapChain(mpWinMain->GetHWND()).WaitForGPU(); - mRenderer.DestroySRV(env.SRV_HDREnvironment); - mRenderer.DestroyTexture(env.Tex_HDREnvironment); - env.MaxContentLightLevel = 0; + UnloadEnvironmentMap(); } const FEnvironmentMapDescriptor& desc = this->GetEnvironmentMapDesc(EnvMapName); - std::vector::iterator it = std::find(mEnvironmentMapPresetNames.begin(), mEnvironmentMapPresetNames.end(), EnvMapName); - const size_t ActiveEnvMapIndex = it - mEnvironmentMapPresetNames.begin(); + std::vector::iterator it = std::find(mResourceNames.mEnvironmentMapPresetNames.begin(), mResourceNames.mEnvironmentMapPresetNames.end(), EnvMapName); + const size_t ActiveEnvMapIndex = it - mResourceNames.mEnvironmentMapPresetNames.begin(); if (!desc.FilePath.empty()) // check whether the env map was found or not { Log::Info("Loading Environment Map: %s", EnvMapName.c_str()); @@ -574,7 +524,8 @@ void VQEngine::LoadEnvironmentMap(const std::string& EnvMapName) env.SRV_HDREnvironment = mRenderer.CreateAndInitializeSRV(env.Tex_HDREnvironment); env.MaxContentLightLevel = static_cast(desc.MaxContentLightLevel); - this->mActiveEnvironmentMapPresetIndex = static_cast(ActiveEnvMapIndex); + //assert(mpScene->mIndex_ActiveEnvironmentMapPreset == static_cast(ActiveEnvMapIndex)); // Only false durin initialization + mpScene->mIndex_ActiveEnvironmentMapPreset = static_cast(ActiveEnvMapIndex); // Update HDRMetaData when the nvironment map is loaded HWND hwnd = mpWinMain->GetHWND(); @@ -588,30 +539,54 @@ void VQEngine::LoadEnvironmentMap(const std::string& EnvMapName) } +SRV_ID FLoadingScreenData::GetSelectedLoadingScreenSRV_ID() const +{ + assert(SelectedLoadingScreenSRVIndex < SRVs.size()); + return SRVs[SelectedLoadingScreenSRVIndex]; +} +void FLoadingScreenData::RotateLoadingScreenImage() +{ + SelectedLoadingScreenSRVIndex = (int)MathUtil::RandU(0, (int)SRVs.size()); +} void VQEngine::LoadLoadingScreenData() { - FLoadingScreenData data; + FLoadingScreenData& data = mLoadingScreenData; data.SwapChainClearColor = { 0.0f, 0.2f, 0.4f, 1.0f }; + constexpr uint NUM_LOADING_SCREEN_BACKGROUNDS = 4; + srand(static_cast(time(NULL))); const std::string LoadingScreenTextureFileDirectory = "Data/Textures/LoadingScreen/"; - const std::string LoadingScreenTextureFilePath = LoadingScreenTextureFileDirectory + (std::to_string(MathUtil::RandU(0, 4)) + ".png"); - TextureID texID = mRenderer.CreateTextureFromFile(LoadingScreenTextureFilePath.c_str()); - SRV_ID srvID = mRenderer.CreateAndInitializeSRV(texID); - data.SRVLoadingScreen = srvID; + const size_t SelectedLoadingScreenIndex = MathUtil::RandU(0u, NUM_LOADING_SCREEN_BACKGROUNDS); - const int NumBackBuffer_WndMain = mRenderer.GetSwapChainBackBufferCount(mpWinMain); - mScene_MainWnd.mLoadingScreenData.resize(NumBackBuffer_WndMain, data); - - if (mpWinDebug) + // dispatch background workers for other + for (size_t i = 0; i < NUM_LOADING_SCREEN_BACKGROUNDS; ++i) { - FLoadingScreenData data; - data.SwapChainClearColor = { 0.5f, 0.4f, 0.01f, 1.0f }; - const int NumBackBuffer_WndDbg = mRenderer.GetSwapChainBackBufferCount(mpWinDebug); - mScene_DebugWnd.mLoadingScreenData.resize(NumBackBuffer_WndDbg, data); + if (i == SelectedLoadingScreenIndex) + continue; // will be loaded on this thread + + const std::string LoadingScreenTextureFilePath = LoadingScreenTextureFileDirectory + (std::to_string(i) + ".png"); + + mWorkers_TextureLoading.AddTask([this, &data, LoadingScreenTextureFilePath]() + { + const TextureID texID = mRenderer.CreateTextureFromFile(LoadingScreenTextureFilePath.c_str()); + const SRV_ID srvID = mRenderer.CreateAndInitializeSRV(texID); + std::lock_guard lk(data.Mtx); + data.SRVs.push_back(srvID); + }); - mWindowUpdateContextLookup[mpWinDebug->GetHWND()] = &mScene_DebugWnd; } + + // load the selected loading screen image + { + const std::string LoadingScreenTextureFilePath = LoadingScreenTextureFileDirectory + (std::to_string(SelectedLoadingScreenIndex) + ".png"); + TextureID texID = mRenderer.CreateTextureFromFile(LoadingScreenTextureFilePath.c_str()); + SRV_ID srvID = mRenderer.CreateAndInitializeSRV(texID); + std::lock_guard lk(data.Mtx); + data.SRVs.push_back(srvID); + data.SelectedLoadingScreenSRVIndex = static_cast(data.SRVs.size() - 1); + } + } diff --git a/Source/Renderer/Buffer.cpp b/Source/Renderer/Buffer.cpp index 2a785c80..8f966a3b 100644 --- a/Source/Renderer/Buffer.cpp +++ b/Source/Renderer/Buffer.cpp @@ -22,6 +22,7 @@ #include "Libs/D3DX12/d3dx12.h" #include +#include size_t StaticBufferHeap::MEMORY_ALIGNMENT = 256; @@ -65,7 +66,7 @@ void StaticBufferHeap::Create(ID3D12Device* pDevice, EBufferType type, uint32 to GetResourceTransitionState(mType), nullptr, IID_PPV_ARGS(&mpVidMemBuffer)); - mpVidMemBuffer->SetName(L"StaticBufferPool::m_pVidMemBuffer"); + SetName(mpVidMemBuffer, name); if (FAILED(hr)) { @@ -88,7 +89,7 @@ void StaticBufferHeap::Create(ID3D12Device* pDevice, EBufferType type, uint32 to // TODO } - mpSysMemBuffer->SetName(L"StaticBufferPool::m_pSysMemBuffer"); + SetName(mpSysMemBuffer, name); mpSysMemBuffer->Map(0, NULL, reinterpret_cast(&mpData)); } @@ -152,7 +153,15 @@ bool StaticBufferHeap::AllocBuffer(uint32 numElements, uint32 strideInBytes, voi std::lock_guard lock(mMtx); uint32 size = AlignOffset(numElements * strideInBytes, (uint32)StaticBufferHeap::MEMORY_ALIGNMENT); - assert(mMemOffset + size < mTotalMemSize); // if this is hit, initialize heap with a larger size. + const bool bHeapOutOfMemory = ((mMemOffset + size) > mTotalMemSize); + //assert( !bHeapOutOfMemory); // if this is hit, initialize heap with a larger size. + if (bHeapOutOfMemory) + { + Log::Error("Static Heap out of memory."); + MessageBox(NULL, "Out of StaticBufferHeap memory", "Error", MB_ICONERROR | MB_OK); + PostMessage(NULL, WM_QUIT, NULL, NULL); + return false; + } *ppDataOut = (void*)(mpData + mMemOffset); @@ -239,7 +248,9 @@ bool DynamicBufferHeap::AllocConstantBuffer(uint32_t size, void** pData, D3D12_G uint32_t memOffset; if (m_mem.Alloc(size, &memOffset) == false) { - Log::Error("Ran out of mem for 'dynamic' buffers, please increase the allocated size\n"); + Log::Error("Ran out of mem for 'dynamic' buffers, increase the allocated size\n"); + MessageBox(NULL, "Out of DynamicBufferHeap memory", "Error", MB_ICONERROR | MB_OK); + PostMessage(NULL, WM_QUIT, NULL, NULL); return false; } diff --git a/Source/Renderer/Renderer.cpp b/Source/Renderer/Renderer.cpp index ea3f49c2..5e624de6 100644 --- a/Source/Renderer/Renderer.cpp +++ b/Source/Renderer/Renderer.cpp @@ -16,6 +16,12 @@ // // Contact: volkanilbeyli@gmail.com + +// Resources for reading +// - http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-descriptor-heaps/ +// - https://asawicki.info/articles/memory_management_vulkan_direct3d_12.php5 +// - https://simonstechblog.blogspot.com/2019/06/d3d12-descriptor-heap-management.html + #include "Renderer.h" #include "Device.h" #include "Texture.h" @@ -56,6 +62,16 @@ const std::string_view& VQRenderer::DXGIFormatAsString(DXGI_FORMAT format) return DXGI_FORMAT_STRING_TRANSLATION.at(format); } +EProceduralTextures VQRenderer::GetProceduralTextureEnumFromName(const std::string& ProceduralTextureName) +{ + static std::unordered_map MAP = + { + { "Checkerboard", EProceduralTextures::CHECKERBOARD } + , { "Checkerboard_Grayscale", EProceduralTextures::CHECKERBOARD_GRAYSCALE } + }; + return MAP.at(ProceduralTextureName); +} + // --------------------------------------------------------------------------------------- @@ -113,20 +129,25 @@ void VQRenderer::Initialize(const FGraphicsSettings& Settings) mComputeQueue.Create(pVQDevice, CommandQueue::ECommandQueueType::COMPUTE); mCopyQueue.Create(pVQDevice, CommandQueue::ECommandQueueType::COPY); - // Initialize memory InitializeD3D12MA(); InitializeHeaps(); + // initialize thread + mbExitUploadThread.store(false); + mbDefaultResourcesLoaded.store(false); + mTextureUploadThread = std::thread(&VQRenderer::TextureUploadThread_Main, this); + Log::Info("[Renderer] Initialized."); - // TODO: Log system info } void VQRenderer::Load() { LoadPSOs(); LoadDefaultResources(); - mHeapUpload.UploadToGPUAndWait(mGFXQueue.pQueue); + + Log::Info("[Renderer] Loaded."); + mbDefaultResourcesLoaded.store(true); } void VQRenderer::Unload() @@ -137,6 +158,9 @@ void VQRenderer::Unload() void VQRenderer::Exit() { + mbExitUploadThread.store(true); + mSignal_UploadThreadWorkReady.NotifyAll(); + mHeapUpload.Destroy(); mHeapCBV_SRV_UAV.Destroy(); mHeapDSV.Destroy(); @@ -185,6 +209,8 @@ void VQRenderer::Exit() mCopyQueue.Destroy(); mDevice.Destroy(); + + mTextureUploadThread.join(); } void VQRenderer::OnWindowSizeChanged(HWND hwnd, unsigned w, unsigned h) @@ -339,12 +365,12 @@ void VQRenderer::InitializeHeaps() { ID3D12Device* pDevice = mDevice.GetDevicePtr(); - const uint32 UPLOAD_HEAP_SIZE = 256 * MEGABYTE; // TODO: from RendererSettings.ini - mHeapUpload.Create(pDevice, UPLOAD_HEAP_SIZE); + const uint32 UPLOAD_HEAP_SIZE = 513 * MEGABYTE; // TODO: from RendererSettings.ini + mHeapUpload.Create(pDevice, UPLOAD_HEAP_SIZE, this->mGFXQueue.pQueue); - constexpr uint32 NumDescsCBV = 10; - constexpr uint32 NumDescsSRV = 10; - constexpr uint32 NumDescsUAV = 10; + constexpr uint32 NumDescsCBV = 100; + constexpr uint32 NumDescsSRV = 3800; + constexpr uint32 NumDescsUAV = 100; constexpr bool bCPUVisible = false; mHeapCBV_SRV_UAV.Create(pDevice, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, NumDescsCBV + NumDescsSRV + NumDescsUAV, bCPUVisible); @@ -354,7 +380,7 @@ void VQRenderer::InitializeHeaps() constexpr uint32 NumDescsRTV = 10; mHeapRTV.Create(pDevice, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, NumDescsRTV); - constexpr uint32 STATIC_GEOMETRY_MEMORY_SIZE = 16 * MEGABYTE; + constexpr uint32 STATIC_GEOMETRY_MEMORY_SIZE = 32 * MEGABYTE; constexpr bool USE_GPU_MEMORY = true; mStaticHeap_VertexBuffer.Create(pDevice, EBufferType::VERTEX_BUFFER, STATIC_GEOMETRY_MEMORY_SIZE, USE_GPU_MEMORY, "VQRenderer::mStaticVertexBufferPool"); mStaticHeap_IndexBuffer .Create(pDevice, EBufferType::INDEX_BUFFER , STATIC_GEOMETRY_MEMORY_SIZE, USE_GPU_MEMORY, "VQRenderer::mStaticIndexBufferPool"); @@ -403,6 +429,7 @@ void VQRenderer::LoadPSOs() ID3D12RootSignature* pRS = nullptr; ThrowIfFailed(pDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&pRS))); mpBuiltinRootSignatures.push_back(pRS); + SetName(pRS, "RootSignature_HelloWorldTriangle"); } // Fullscreen-Triangle Root Signature : [1] @@ -446,6 +473,7 @@ void VQRenderer::LoadPSOs() ID3D12RootSignature* pRS = nullptr; ThrowIfFailed(pDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&pRS))); mpBuiltinRootSignatures.push_back(pRS); + SetName(pRS, "RootSignature_FSTriangle"); } // Hello-World-Cube Root Signature : [2] @@ -492,6 +520,7 @@ void VQRenderer::LoadPSOs() ID3D12RootSignature* pRS = nullptr; ThrowIfFailed(pDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&pRS))); mpBuiltinRootSignatures.push_back(pRS); + SetName(pRS, "RootSignature_HelloWorldCube"); } // Tonemapper Root Signature : [3] @@ -525,6 +554,59 @@ void VQRenderer::LoadPSOs() ID3D12RootSignature* pRS = nullptr; ThrowIfFailed(pDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&pRS))); mpBuiltinRootSignatures.push_back(pRS); + SetName(pRS, "RootSignature_Tonemapper"); + } + + // Object Root Signature : [4] + { + D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {}; + + // This is the highest version the sample supports. If CheckFeatureSupport succeeds, the HighestVersion returned will not be greater than this. + featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1; + if (FAILED(pDevice->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &featureData, sizeof(featureData)))) + { + featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0; + } + + CD3DX12_DESCRIPTOR_RANGE1 ranges[2]; + ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 6, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE/*D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC*/); + ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); + + CD3DX12_ROOT_PARAMETER1 rootParameters[2]; + rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL); + rootParameters[1].InitAsConstantBufferView(0, 0, D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE, D3D12_SHADER_VISIBILITY_ALL); + + D3D12_STATIC_SAMPLER_DESC samplers[2] = {}; + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + samplers[0] = sampler; + + sampler.ShaderRegister = 1; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + samplers[1] = sampler; + + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc; + rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, _countof(samplers), &samplers[0], D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); + + ComPtr signature; + ComPtr error; + ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error)); + ID3D12RootSignature* pRS = nullptr; + ThrowIfFailed(pDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&pRS))); + mpBuiltinRootSignatures.push_back(pRS); + SetName(pRS, "RootSignature_Object"); } } @@ -612,6 +694,10 @@ void VQRenderer::LoadPSOs() if (FAILED(hr)) { ReportErrorAndReleaseBlob(errBlob); } ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"hello-cube.hlsl").c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, &errBlob)); + // TEST REFLECTION + //D3DReflect(vertexShader->GetBufferPointer(), vertexShader->GetBufferSize(), IID_ID3D12ShaderReflection, ppBuffer); + //vertexShader + // Define the vertex input layout. D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = { @@ -713,6 +799,52 @@ void VQRenderer::LoadPSOs() ThrowIfFailed(pDevice->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&mpBuiltinPSOs[EBuiltinPSOs::TONEMAPPER_PSO]))); SetName(mpBuiltinPSOs[EBuiltinPSOs::HELLO_WORLD_CUBE_PSO], "PSO_TonemapperCS"); } + + // OBJECT PSO + { + ComPtr vertexShader; + ComPtr pixelShader; + ComPtr errBlob; + + hr = D3DCompileFromFile(GetAssetFullPath(L"Object.hlsl").c_str(), nullptr, nullptr, "VSMain", "vs_5_1", compileFlags, 0, &vertexShader, &errBlob); + if (FAILED(hr)) { ReportErrorAndReleaseBlob(errBlob); } + ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"Object.hlsl").c_str(), nullptr, nullptr, "PSMain", "ps_5_1", compileFlags, 0, &pixelShader, &errBlob)); + + // Define the vertex input layout. + D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT , 0, 0 , D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "NORMAL" , 0, DXGI_FORMAT_R32G32B32_FLOAT , 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TANGENT" , 0, DXGI_FORMAT_R32G32B32_FLOAT , 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT , 0, 36, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + }; + + // Describe and create the graphics pipeline state object (PSO). + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) }; + psoDesc.pRootSignature = mpBuiltinRootSignatures[4]; + psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get()); + psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get()); + psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); + psoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; + psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); + psoDesc.DepthStencilState.DepthEnable = TRUE; + psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS; + psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + psoDesc.DepthStencilState.StencilEnable = FALSE; + psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT; + psoDesc.SampleMask = UINT_MAX; + psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + psoDesc.NumRenderTargets = 1; + psoDesc.RTVFormats[0] = DXGI_FORMAT_R16G16B16A16_FLOAT; + psoDesc.SampleDesc.Count = 1; + ThrowIfFailed(pDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mpBuiltinPSOs[EBuiltinPSOs::OBJECT_PSO]))); + SetName(mpBuiltinPSOs[EBuiltinPSOs::OBJECT_PSO], "PSO_Object"); + + psoDesc.SampleDesc.Count = 4; + ThrowIfFailed(pDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mpBuiltinPSOs[EBuiltinPSOs::OBJECT_PSO_MSAA_4]))); + SetName(mpBuiltinPSOs[EBuiltinPSOs::OBJECT_PSO_MSAA_4], "PSO_Object_MSAA4"); + } } void VQRenderer::LoadDefaultResources() @@ -738,13 +870,23 @@ void VQRenderer::LoadDefaultResources() textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE; } - // programmatically generated texture + // programmatically generated textures { std::vector texture = Texture::GenerateTexture_Checkerboard(sizeX); TextureID texID = this->CreateTexture("Checkerboard", textureDesc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, texture.data()); - this->CreateAndInitializeSRV(texID); + mLookup_ProceduralTextureIDs[EProceduralTextures::CHECKERBOARD] = texID; + mLookup_ProceduralTextureSRVs[EProceduralTextures::CHECKERBOARD] = this->CreateAndInitializeSRV(texID); } - + { + std::vector texture = Texture::GenerateTexture_Checkerboard(sizeX, true); + TextureID texID = this->CreateTexture("Checkerboard_Gray", textureDesc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, texture.data()); + mLookup_ProceduralTextureIDs[EProceduralTextures::CHECKERBOARD_GRAYSCALE] = texID; + mLookup_ProceduralTextureSRVs[EProceduralTextures::CHECKERBOARD_GRAYSCALE] = this->CreateAndInitializeSRV(texID); + } +} +void VQRenderer::UploadVertexAndIndexBufferHeaps() +{ + std::lock_guard lk(mMtxTextureUploadQueue); mStaticHeap_VertexBuffer.UploadData(mHeapUpload.GetCommandList()); mStaticHeap_IndexBuffer.UploadData(mHeapUpload.GetCommandList()); } @@ -764,7 +906,16 @@ ID3D12DescriptorHeap* VQRenderer::GetDescHeap(EResourceHeapType HeapType) } - +TextureID VQRenderer::GetProceduralTexture(EProceduralTextures tex) const +{ + while (!mbDefaultResourcesLoaded); + if (mLookup_ProceduralTextureIDs.find(tex) == mLookup_ProceduralTextureIDs.end()) + { + Log::Error("Couldn't find procedural texture %d", tex); + return INVALID_ID; + } + return mLookup_ProceduralTextureIDs.at(tex); +} diff --git a/Source/Renderer/Renderer.h b/Source/Renderer/Renderer.h index de92fa28..296179f5 100644 --- a/Source/Renderer/Renderer.h +++ b/Source/Renderer/Renderer.h @@ -32,10 +32,13 @@ #define VQUTILS_SYSTEMINFO_INCLUDE_D3D12 1 #include "../../Libs/VQUtils/Source/SystemInfo.h" // FGPUInfo +#include "../../Libs/VQUtils/Source/Image.h" +#include "../../Libs/VQUtils/Source/Multithreading.h" #include #include #include +#include namespace D3D12MA { class Allocator; } class Window; @@ -43,7 +46,6 @@ struct ID3D12RootSignature; struct ID3D12PipelineState; - // // TYPE DEFINITIONS // @@ -73,6 +75,18 @@ struct FWindowRenderContext int MainRTResolutionY = -1; }; +struct FTextureUploadDesc +{ + FTextureUploadDesc(Image&& img_ , TextureID texID, const TextureCreateDesc& tDesc) : img(img_), id(texID), desc(tDesc), pData(nullptr) {} + FTextureUploadDesc(const void* pData_, TextureID texID, const TextureCreateDesc& tDesc) : img({ }), id(texID), desc(tDesc), pData(pData_) {} + FTextureUploadDesc() = delete; + + Image img; + const void* pData; + TextureID id; + TextureCreateDesc desc; +}; + enum EBuiltinPSOs // TODO: hardcoded PSOs until a generic Shader solution is integrated { HELLO_WORLD_TRIANGLE_PSO = 0, @@ -83,10 +97,20 @@ enum EBuiltinPSOs // TODO: hardcoded PSOs until a generic Shader solution is int HDR_FP16_SWAPCHAIN_PSO, SKYDOME_PSO, SKYDOME_PSO_MSAA_4, + OBJECT_PSO, + OBJECT_PSO_MSAA_4, NUM_BUILTIN_PSOs }; +enum EProceduralTextures +{ + CHECKERBOARD = 0 + , CHECKERBOARD_GRAYSCALE + + , NUM_PROCEDURAL_TEXTURES +}; + // // RENDERER @@ -98,6 +122,7 @@ class VQRenderer void Load(); void Unload(); void Exit(); + inline void WaitForLoadCompletion() const { while (!mbDefaultResourcesLoaded); }; void OnWindowSizeChanged(HWND hwnd, unsigned w, unsigned h); @@ -113,6 +138,7 @@ class VQRenderer BufferID CreateBuffer(const FBufferDesc& desc); TextureID CreateTextureFromFile(const char* pFilePath); TextureID CreateTexture(const std::string& name, const D3D12_RESOURCE_DESC& desc, D3D12_RESOURCE_STATES ResourceState, const void* pData = nullptr); + void UploadVertexAndIndexBufferHeaps(); // Allocates a ResourceView from the respective heap and returns a unique identifier. SRV_ID CreateSRV(uint NumDescriptors = 1); @@ -157,6 +183,16 @@ class VQRenderer inline const RTV& GetRTV(RTV_ID Id) const { return GetRenderTargetView(Id); } inline const DSV& GetDSV(DSV_ID Id) const { return GetDepthStencilView(Id); } + inline const SRV& GetProceduralTextureSRV(EProceduralTextures tex) const { return GetSRV(GetProceduralTextureSRV_ID(tex)); } + inline const SRV_ID GetProceduralTextureSRV_ID(EProceduralTextures tex) const { return mLookup_ProceduralTextureSRVs.at(tex); } + TextureID GetProceduralTexture(EProceduralTextures tex) const; + + // Texture Residency + void QueueTextureUpload(const FTextureUploadDesc& desc); + void ProcessTextureUploadQueue(); + void TextureUploadThread_Main(); + inline void StartTextureUploads() { mSignal_UploadThreadWorkReady.NotifyOne(); }; + private: using PSOArray_t = std::array; @@ -177,6 +213,7 @@ class VQRenderer StaticBufferHeap mStaticHeap_IndexBuffer; // resources & views + std::unordered_map mLoadedTexturePaths; std::unordered_map mTextures; std::unordered_map mSamplers; std::unordered_map mVBVs; @@ -197,6 +234,8 @@ class VQRenderer mutable std::mutex mMtxVBVs; mutable std::mutex mMtxIBVs; + mutable std::mutex mMtxUploadHeapCreation; + // root signatures std::vector mpBuiltinRootSignatures; @@ -207,9 +246,17 @@ class VQRenderer std::unordered_map mRenderContextLookup; // bookkeeping - std::unordered_map mLookup_TextureDiskLocations; + std::unordered_map mLookup_TextureDiskLocations; + std::unordered_map mLookup_ProceduralTextureSRVs; + std::unordered_map mLookup_ProceduralTextureIDs; + std::atomic mbExitUploadThread; + Signal mSignal_UploadThreadWorkReady; + std::thread mTextureUploadThread; + std::mutex mMtxTextureUploadQueue; + std::queue mTextureUploadQueue; + std::atomic mbDefaultResourcesLoaded; private: void InitializeD3D12MA(); @@ -232,4 +279,5 @@ class VQRenderer public: static std::vector< VQSystemInfo::FGPUInfo > EnumerateDX12Adapters(bool bEnableDebugLayer, bool bEnumerateSoftwareAdapters = false, IDXGIFactory6* pFactory = nullptr); static const std::string_view& DXGIFormatAsString(DXGI_FORMAT format); + static EProceduralTextures GetProceduralTextureEnumFromName(const std::string& ProceduralTextureName); }; \ No newline at end of file diff --git a/Source/Renderer/Renderer_Resources.cpp b/Source/Renderer/Renderer_Resources.cpp index dada5721..228933b5 100644 --- a/Source/Renderer/Renderer_Resources.cpp +++ b/Source/Renderer/Renderer_Resources.cpp @@ -24,6 +24,8 @@ #include "../../Libs/VQUtils/Source/Log.h" #include "../../Libs/VQUtils/Source/utils.h" +#include "../../Libs/VQUtils/Source/Timer.h" +#include "../../Libs/VQUtils/Source/Image.h" #include "../../Libs/D3D12MA/src/Common.h" #include @@ -39,6 +41,8 @@ using namespace VQSystemInfo; #define ENABLE_DEBUG_LAYER 0 #define ENABLE_VALIDATION_LAYER 0 #endif +#define LOG_CACHED_RESOURCES_ON_LOAD 0 +#define LOG_RESOURCE_CREATE 1 // TODO: initialize from functions? static TextureID LAST_USED_TEXTURE_ID = 0; @@ -72,59 +76,101 @@ BufferID VQRenderer::CreateBuffer(const FBufferDesc& desc) TextureID VQRenderer::CreateTextureFromFile(const char* pFilePath) { - // TODO: check if file already loaded + // check if we've already loaded the texture + auto it = mLoadedTexturePaths.find(pFilePath); + if (it != mLoadedTexturePaths.end()) + { +#if LOG_CACHED_RESOURCES_ON_LOAD + Log::Info("Texture already loaded: %s", pFilePath); +#endif + return it->second; + } + // check path + if (strlen(pFilePath) == 0) + { + Log::Warning("VQRenderer::CreateTextureFromFile: Empty FilePath provided"); + return INVALID_ID; + } - Texture tex; + // -------------------------------------------------------- + + TextureID ID = INVALID_ID; - // https://docs.microsoft.com/en-us/windows/win32/direct3d12/residency#heap-resources - // Heap creation can be slow; but it is optimized for background thread processing. - // It's recommended to create heaps on background threads to avoid glitching the render - // thread. In D3D12, multiple threads may safely call create routines concurrently. - UploadHeap uploadHeap; - uploadHeap.Create(mDevice.GetDevicePtr(), 1024 * MEGABYTE); // TODO: drive the heapsize through RendererSettings.ini + Timer t; t.Start(); + Texture tex; const std::string FileNameAndExtension = DirectoryUtil::GetFileNameFromPath(pFilePath); TextureCreateDesc tDesc(FileNameAndExtension); tDesc.pAllocator = mpAllocator; tDesc.pDevice = mDevice.GetDevicePtr(); - tDesc.pUploadHeap = &uploadHeap; - tDesc.Desc = {}; - bool bSuccess = tex.CreateFromFile(tDesc, pFilePath); - TextureID ID = INVALID_ID; + Image image; + const bool bSuccess = Texture::ReadImageFromDisk(pFilePath, image); + const bool bHDR = image.BytesPerPixel > 4; if (bSuccess) { - uploadHeap.UploadToGPUAndWait(mGFXQueue.pQueue); + // Fill D3D12 Descriptor + tDesc.d3d12Desc = {}; + tDesc.d3d12Desc.Width = image.Width; + tDesc.d3d12Desc.Height = image.Height; + tDesc.d3d12Desc.Format = bHDR ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM; + tDesc.d3d12Desc.DepthOrArraySize = 1; + tDesc.d3d12Desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + tDesc.d3d12Desc.Alignment = 0; + tDesc.d3d12Desc.DepthOrArraySize = 1; + tDesc.d3d12Desc.MipLevels = 1; + tDesc.d3d12Desc.SampleDesc.Count = 1; + tDesc.d3d12Desc.SampleDesc.Quality = 0; + tDesc.d3d12Desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + tDesc.d3d12Desc.Flags = D3D12_RESOURCE_FLAG_NONE; + + tex.Create(tDesc, image.pData); ID = AddTexture_ThreadSafe(std::move(tex)); + this->QueueTextureUpload(FTextureUploadDesc(std::move(image), ID, tDesc)); + + this->StartTextureUploads(); + std::atomic& bResident = mTextures.at(ID).bResident; + while (!bResident.load()); // busy wait here until the texture is made resident; + +#if LOG_RESOURCE_CREATE + Log::Info("VQRenderer::CreateTextureFromFile(): [%.2fs] %s", t.StopGetDeltaTimeAndReset(), pFilePath); +#endif } - uploadHeap.Destroy(); + return ID; } TextureID VQRenderer::CreateTexture(const std::string& name, const D3D12_RESOURCE_DESC& desc, D3D12_RESOURCE_STATES ResourceState, const void* pData) { Texture tex; - - // https://docs.microsoft.com/en-us/windows/win32/direct3d12/residency#heap-resources - // Heap creation can be slow; but it is optimized for background thread processing. - // It's recommended to create heaps on background threads to avoid glitching the render - // thread. In D3D12, multiple threads may safely call create routines concurrently. - UploadHeap uploadHeap; - uploadHeap.Create(mDevice.GetDevicePtr(), 32 * MEGABYTE); // TODO: drive the heapsize through RendererSettings.ini + Timer t; t.Start(); TextureCreateDesc tDesc(name); - tDesc.Desc = desc; + tDesc.d3d12Desc = desc; tDesc.pAllocator = mpAllocator; tDesc.pDevice = mDevice.GetDevicePtr(); - tDesc.pUploadHeap = &uploadHeap; tDesc.ResourceState = ResourceState; tex.Create(tDesc, pData); - uploadHeap.UploadToGPUAndWait(mGFXQueue.pQueue); - uploadHeap.Destroy(); + TextureID ID = AddTexture_ThreadSafe(std::move(tex)); + if (pData) + { + this->QueueTextureUpload(FTextureUploadDesc(pData, ID, tDesc)); + + this->StartTextureUploads(); + std::atomic& bResident = mTextures.at(ID).bResident; + while (!bResident.load()); // busy wait here until the texture is made resident; + } - return AddTexture_ThreadSafe(std::move(tex)); + if (pData) + { +#if LOG_RESOURCE_CREATE + Log::Info("VQRenderer::CreateTexture(): [%.2fs] %s", t.StopGetDeltaTimeAndReset(), name.c_str()); +#endif + } + + return ID; } SRV_ID VQRenderer::CreateAndInitializeSRV(TextureID texID) { @@ -135,7 +181,7 @@ SRV_ID VQRenderer::CreateAndInitializeSRV(TextureID texID) std::lock_guard lk(mMtxSRVs_CBVs_UAVs); mHeapCBV_SRV_UAV.AllocDescriptor(1, &SRV); - mTextures[texID].InitializeSRV(0, &SRV); + mTextures.at(texID).InitializeSRV(0, &SRV); Id = LAST_USED_SRV_ID++; mSRVs[Id] = SRV; } @@ -238,9 +284,25 @@ void VQRenderer::InitializeDSV(DSV_ID dsvID, uint32 heapIndex, TextureID texID) } void VQRenderer::InitializeSRV(SRV_ID srvID, uint heapIndex, TextureID texID) { - CHECK_TEXTURE(mTextures, texID); CHECK_RESOURCE_VIEW(SRV, srvID); - mTextures.at(texID).InitializeSRV(heapIndex, &mSRVs.at(srvID)); + if (texID != INVALID_ID) + { + CHECK_TEXTURE(mTextures, texID); + mTextures.at(texID).InitializeSRV(heapIndex, &mSRVs.at(srvID)); + } + else // init NULL SRV + { + // Describe and create 2 null SRVs. Null descriptors are needed in order + // to achieve the effect of an "unbound" resource. + D3D12_SHADER_RESOURCE_VIEW_DESC nullSrvDesc = {}; + nullSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + nullSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + nullSrvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + nullSrvDesc.Texture2D.MipLevels = 1; + nullSrvDesc.Texture2D.MostDetailedMip = 0; + nullSrvDesc.Texture2D.ResourceMinLODClamp = 0.0f; + mDevice.GetDevicePtr()->CreateShaderResourceView(nullptr, &nullSrvDesc, mSRVs.at(srvID).GetCPUDescHandle(heapIndex)); + } } void VQRenderer::InitializeRTV(RTV_ID rtvID, uint heapIndex, TextureID texID) { @@ -259,7 +321,7 @@ void VQRenderer::InitializeUAV(UAV_ID uavID, uint heapIndex, TextureID texID) BufferID VQRenderer::CreateVertexBuffer(const FBufferDesc& desc) { BufferID Id = INVALID_ID; - VBV vbv; + VBV vbv = {}; std::lock_guard lk(mMtxStaticVBHeap); @@ -270,7 +332,7 @@ BufferID VQRenderer::CreateVertexBuffer(const FBufferDesc& desc) mVBVs[Id] = vbv; } else - Log::Error("Couldn't allocate vertex buffer"); + Log::Error("VQRenderer: Couldn't allocate vertex buffer"); return Id; } BufferID VQRenderer::CreateIndexBuffer(const FBufferDesc& desc) @@ -340,6 +402,104 @@ ID3D12Resource* VQRenderer::GetTextureResource(TextureID Id) return mTextures.at(Id).GetResource(); } +void VQRenderer::QueueTextureUpload(const FTextureUploadDesc& desc) +{ + std::unique_lock lk(mMtxTextureUploadQueue); + mTextureUploadQueue.push(desc); +} + +void VQRenderer::ProcessTextureUploadQueue() +{ + std::unique_lock lk(mMtxTextureUploadQueue); + if (mTextureUploadQueue.empty()) + return; + + ID3D12GraphicsCommandList* pCmd = mHeapUpload.GetCommandList(); + ID3D12Device* pDevice = mDevice.GetDevicePtr(); + + std::vector*> vTexResidentBools; + std::vector vImages; + + while (!mTextureUploadQueue.empty()) + { + FTextureUploadDesc desc = std::move(mTextureUploadQueue.front()); + mTextureUploadQueue.pop(); + + ID3D12Resource* pResc = GetTextureResource(desc.id); + const void* pData = desc.img.pData ? desc.img.pData : desc.pData; + assert(pData); + + const UINT64 UploadBufferSize = GetRequiredIntermediateSize(pResc, 0, 1); + + UINT8* pUploadBufferMem = mHeapUpload.Suballocate(SIZE_T(UploadBufferSize), D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); + if (pUploadBufferMem == NULL) + { + // We ran out of mem in the upload heap, flush it and try allocating mem from it again + mHeapUpload.UploadToGPUAndWait(); + pUploadBufferMem = mHeapUpload.Suballocate(SIZE_T(UploadBufferSize), D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); + assert(pUploadBufferMem); + } + + UINT64 UplHeapSize; + uint32_t num_rows = {}; + UINT64 row_size_in_bytes = {}; + D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTex2D = {}; + D3D12_RESOURCE_DESC d3dDesc = {}; + + pDevice->GetCopyableFootprints(&desc.desc.d3d12Desc, 0, 1, 0, &placedTex2D, &num_rows, &row_size_in_bytes, &UplHeapSize); + placedTex2D.Offset += UINT64(pUploadBufferMem - mHeapUpload.BasePtr()); + + // copy data row by row + for (uint32_t y = 0; y < num_rows; y++) + { + const UINT64 UploadMemOffset = y * placedTex2D.Footprint.RowPitch; + const UINT64 DataMemOffset = y * row_size_in_bytes; + memcpy(pUploadBufferMem + UploadMemOffset, (UINT8*)pData + DataMemOffset, row_size_in_bytes); + } + + CD3DX12_TEXTURE_COPY_LOCATION Dst(pResc, 0); + CD3DX12_TEXTURE_COPY_LOCATION Src(mHeapUpload.GetResource(), placedTex2D); + pCmd->CopyTextureRegion(&Dst, 0, 0, 0, &Src, NULL); + + D3D12_RESOURCE_BARRIER textureBarrier = {}; + textureBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + textureBarrier.Transition.pResource = pResc; + textureBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + textureBarrier.Transition.StateAfter = desc.desc.ResourceState; + textureBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + pCmd->ResourceBarrier(1, &textureBarrier); + + Texture& tex = mTextures.at(desc.id); + vTexResidentBools.push_back(&tex.bResident); + + if (desc.img.pData) + { + vImages.push_back(std::move(desc.img)); + } + } + + mHeapUpload.UploadToGPUAndWait(); + + for (std::atomic* pbResident : vTexResidentBools) + pbResident->store(true); + + for (Image& img : vImages) + img.Destroy(); // free the image memory +} + +void VQRenderer::TextureUploadThread_Main() +{ + while (!mbExitUploadThread) + { + mSignal_UploadThreadWorkReady.Wait([&]() { return mbExitUploadThread.load() || !mTextureUploadQueue.empty(); }); + + if (mbExitUploadThread) + break; + + this->ProcessTextureUploadQueue(); + } +} + TextureID VQRenderer::AddTexture_ThreadSafe(Texture&& tex) { @@ -347,14 +507,32 @@ TextureID VQRenderer::AddTexture_ThreadSafe(Texture&& tex) std::lock_guard lk(mMtxTextures); Id = LAST_USED_TEXTURE_ID++; - mTextures[Id] = tex; + + mTextures[Id] = std::move(tex); + return Id; } void VQRenderer::DestroyTexture(TextureID texID) { + // Remove texID std::lock_guard lk(mMtxTextures); mTextures.at(texID).Destroy(); mTextures.erase(texID); + + // Remove texture path from cache + std::string texPath = ""; + bool bTexturePathRegistered = false; + for (const auto& path_id_pair : mLoadedTexturePaths) + { + if (path_id_pair.second == texID) + { + texPath = path_id_pair.first; + bTexturePathRegistered = true; + break; + } + } + if (bTexturePathRegistered) + mLoadedTexturePaths.erase(texPath); } void VQRenderer::DestroySRV(SRV_ID srvID) { diff --git a/Source/Renderer/ResourceHeaps.cpp b/Source/Renderer/ResourceHeaps.cpp index d5f7bdfe..2b1f1f5f 100644 --- a/Source/Renderer/ResourceHeaps.cpp +++ b/Source/Renderer/ResourceHeaps.cpp @@ -109,9 +109,10 @@ bool StaticResourceViewHeap::AllocDescriptor(uint32 size, ResourceView* pRV) // UploadHeap // //-------------------------------------------------------------------------------------- -void UploadHeap::Create(ID3D12Device* pDevice, SIZE_T uSize) +void UploadHeap::Create(ID3D12Device* pDevice, SIZE_T uSize, ID3D12CommandQueue* pQueue) { mpDevice = pDevice; + mpQueue = pQueue; // Create command list and allocators @@ -177,8 +178,13 @@ UINT8* UploadHeap::Suballocate(SIZE_T uSize, UINT64 uAlign) return pRet; } -void UploadHeap::UploadToGPUAndWait(ID3D12CommandQueue* pCmdQueue) +void UploadHeap::UploadToGPUAndWait(ID3D12CommandQueue* pCmdQueue /* =nullptr */) { + if (!pCmdQueue) + { + pCmdQueue = this->mpQueue; + } + mpCommandList->Close(); pCmdQueue->ExecuteCommandLists(1, CommandListCast(&mpCommandList)); pCmdQueue->Signal(mpFence, mFenceValue); diff --git a/Source/Renderer/ResourceHeaps.h b/Source/Renderer/ResourceHeaps.h index 53017e18..beb05f67 100644 --- a/Source/Renderer/ResourceHeaps.h +++ b/Source/Renderer/ResourceHeaps.h @@ -77,7 +77,7 @@ class StaticResourceViewHeap class UploadHeap { public: - void Create(ID3D12Device* pDevice, SIZE_T uSize); + void Create(ID3D12Device* pDevice, SIZE_T uSize, ID3D12CommandQueue* pQueue); void Destroy(); UINT8* Suballocate(SIZE_T uSize, UINT64 uAlign); @@ -86,11 +86,12 @@ class UploadHeap inline ID3D12Resource* GetResource() const { return mpUploadHeap; } inline ID3D12GraphicsCommandList* GetCommandList() const { return mpCommandList; } - void UploadToGPUAndWait(ID3D12CommandQueue* pCmdQueue); + void UploadToGPUAndWait(ID3D12CommandQueue* pCmdQueue = nullptr); private: ID3D12Device* mpDevice = nullptr; ID3D12Resource* mpUploadHeap = nullptr; + ID3D12CommandQueue* mpQueue = nullptr; ID3D12GraphicsCommandList* mpCommandList = nullptr; ID3D12CommandAllocator* mpCommandAllocator = nullptr; diff --git a/Source/Renderer/SwapChain.cpp b/Source/Renderer/SwapChain.cpp index 7948ea27..4f98d4d7 100644 --- a/Source/Renderer/SwapChain.cpp +++ b/Source/Renderer/SwapChain.cpp @@ -520,7 +520,7 @@ void SwapChain::MoveToNextFrame() Log::Warning("SwapChain : next frame not ready. FenceComplVal=%d < FenceVal[curr]=%d", fenceComplVal, mFenceValues[mICurrentBackBuffer]); #endif ThrowIfFailed(mpFence->SetEventOnCompletion(mFenceValues[mICurrentBackBuffer], mHEvent)); - hr = WaitForSingleObjectEx(mHEvent, 200, FALSE); + hr = WaitForSingleObjectEx(mHEvent, 800, FALSE); } switch (hr) { diff --git a/Source/Renderer/Texture.cpp b/Source/Renderer/Texture.cpp index 0e52fcd8..80046843 100644 --- a/Source/Renderer/Texture.cpp +++ b/Source/Renderer/Texture.cpp @@ -29,16 +29,26 @@ #include #include +Texture::Texture(const Texture& other) + : mpAlloc(other.mpAlloc) + , mpTexture(other.mpTexture) + , bResident(other.bResident.load()) +{} -// -// TEXTURE -// -bool Texture::CreateFromFile(const TextureCreateDesc& tDesc, const std::string& FilePath) +Texture& Texture::operator=(const Texture& other) { + mpAlloc = other.mpAlloc; + mpTexture = other.mpTexture; + bResident = other.bResident.load(); + return *this; +} + +bool Texture::ReadImageFromDisk(const std::string& FilePath, Image& img) +{ if (FilePath.empty()) { - Log::Error("Cannot create Texture from file: empty FilePath provided."); + Log::Error("Cannot load Image from file: empty FilePath provided."); return false; } @@ -58,46 +68,20 @@ bool Texture::CreateFromFile(const TextureCreateDesc& tDesc, const std::string& assert(FileExtension != "exr"); // TODO: add exr loading support to Image class const bool bHDR = S_HDR_FORMATS.find(FileExtension) != S_HDR_FORMATS.end(); - // load img - Image image = Image::LoadFromFile(FilePath.c_str(), bHDR); - const bool bImageLoadSucceeded = image.pData && image.BytesPerPixel > 0; - if (bImageLoadSucceeded) - { - TextureCreateDesc desc = tDesc; - desc.Desc.Width = image.Width; - desc.Desc.Height = image.Height; - desc.Desc.Format = bHDR ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM; - //------------------------------- - desc.Desc.DepthOrArraySize = 1; - desc.Desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - desc.Desc.Alignment = 0; - desc.Desc.DepthOrArraySize = 1; - desc.Desc.MipLevels = 1; - desc.Desc.SampleDesc.Count = 1; - desc.Desc.SampleDesc.Quality = 0; - desc.Desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - desc.Desc.Flags = D3D12_RESOURCE_FLAG_NONE; - //------------------------------- - Create(desc, image.pData); - } - else - { - Log::Error("Texture::CreateFromFile() : TexName=%s, FileName=%s", tDesc.TexName.c_str(), FileName.c_str() ); - } - image.Destroy(); - return bImageLoadSucceeded; + img = Image::LoadFromFile(FilePath.c_str(), bHDR); + return img.pData && img.BytesPerPixel > 0; } -// TODO: clean up function +// +// TEXTURE +// void Texture::Create(const TextureCreateDesc& desc, const void* pData /*= nullptr*/) { HRESULT hr = {}; - ID3D12GraphicsCommandList* pCmd = desc.pUploadHeap->GetCommandList(); - - const bool bDepthStencilTexture = desc.Desc.Format == DXGI_FORMAT_R32_TYPELESS; // TODO: change this? - const bool bRenderTargetTexture = (desc.Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) != 0; - const bool bUnorderedAccessTexture = (desc.Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) != 0; + const bool bDepthStencilTexture = desc.d3d12Desc.Format == DXGI_FORMAT_R32_TYPELESS; // TODO: change this? + const bool bRenderTargetTexture = (desc.d3d12Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) != 0; + const bool bUnorderedAccessTexture = (desc.d3d12Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) != 0; // determine resource state & optimal clear value D3D12_RESOURCE_STATES ResourceState = pData @@ -107,7 +91,7 @@ void Texture::Create(const TextureCreateDesc& desc, const void* pData /*= nullpt if (bDepthStencilTexture) { D3D12_CLEAR_VALUE ClearValue = {}; - ClearValue.Format = (desc.Desc.Format == DXGI_FORMAT_R32_TYPELESS) ? DXGI_FORMAT_D32_FLOAT : desc.Desc.Format; + ClearValue.Format = (desc.d3d12Desc.Format == DXGI_FORMAT_R32_TYPELESS) ? DXGI_FORMAT_D32_FLOAT : desc.d3d12Desc.Format; ClearValue.DepthStencil.Depth = 1.0f; ClearValue.DepthStencil.Stencil = 0; pClearValue = &ClearValue; @@ -115,7 +99,7 @@ void Texture::Create(const TextureCreateDesc& desc, const void* pData /*= nullpt if (bRenderTargetTexture) { D3D12_CLEAR_VALUE ClearValue = {}; - ClearValue.Format = desc.Desc.Format; + ClearValue.Format = desc.d3d12Desc.Format; pClearValue = &ClearValue; } if (bUnorderedAccessTexture) @@ -129,7 +113,7 @@ void Texture::Create(const TextureCreateDesc& desc, const void* pData /*= nullpt textureAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; hr = desc.pAllocator->CreateResource( &textureAllocDesc, - &desc.Desc, + &desc.d3d12Desc, ResourceState, pClearValue, &mpAlloc, @@ -140,64 +124,6 @@ void Texture::Create(const TextureCreateDesc& desc, const void* pData /*= nullpt return; } SetName(mpTexture, desc.TexName.c_str()); - - // upload the data - if (pData) - { - const UINT64 UploadBufferSize = GetRequiredIntermediateSize(mpTexture, 0, 1); - -#if 1 - UINT8* pUploadBufferMem = desc.pUploadHeap->Suballocate(SIZE_T(UploadBufferSize), D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - if (pUploadBufferMem == NULL) - { - - // TODO: - // We ran out of mem in the upload heap, flush it and try allocating mem from it again -#if 0 - desc.pUploadHeap->UploadToGPUAndWait(); - pUploadBufferMem = desc.pUploadHeap->Suballocate(SIZE_T(UploadBufferSize), D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - assert(pUploadBufferMem); -#else - assert(false); -#endif - } - - UINT64 UplHeapSize; - uint32_t num_rows = {}; - UINT64 row_size_in_bytes = {}; - D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTex2D = {}; - desc.pDevice->GetCopyableFootprints(&desc.Desc, 0, 1, 0, &placedTex2D, &num_rows, &row_size_in_bytes, &UplHeapSize); - placedTex2D.Offset += UINT64(pUploadBufferMem - desc.pUploadHeap->BasePtr()); - - // copy data row by row - for (uint32_t y = 0; y < num_rows; y++) - { - const UINT64 UploadMemOffset = y * placedTex2D.Footprint.RowPitch; - const UINT64 DataMemOffset = y * row_size_in_bytes; - memcpy(pUploadBufferMem + UploadMemOffset, (UINT8*)pData + DataMemOffset, row_size_in_bytes); - } - - CD3DX12_TEXTURE_COPY_LOCATION Dst(mpTexture, 0); - CD3DX12_TEXTURE_COPY_LOCATION Src(desc.pUploadHeap->GetResource(), placedTex2D); - desc.pUploadHeap->GetCommandList()->CopyTextureRegion(&Dst, 0, 0, 0, &Src, NULL); - -#else - D3D12_SUBRESOURCE_DATA textureSubresourceData = {}; - textureSubresourceData.pData = pData; - textureSubresourceData.RowPitch = imageBytesPerRow; - textureSubresourceData.SlicePitch = imageBytesPerRow * desc.Desc.Height; - - UpdateSubresources(pCmd, mpTexture, textureUpload, 0, 0, 1, &textureSubresourceData); -#endif - - D3D12_RESOURCE_BARRIER textureBarrier = {}; - textureBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - textureBarrier.Transition.pResource = mpTexture; - textureBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; - textureBarrier.Transition.StateAfter = desc.ResourceState; - textureBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - pCmd->ResourceBarrier(1, &textureBarrier); - } } @@ -278,6 +204,7 @@ void Texture::InitializeUAV(uint32 index, CBV_SRV_UAV* pRV, D3D12_UNORDERED_ACCE pDevice->Release(); } + #if 0 void Texture::CreateUAV(uint32_t index, Texture* pCounterTex, CBV_SRV_UAV* pRV, D3D12_UNORDERED_ACCESS_VIEW_DESC* pUavDesc) { @@ -328,7 +255,7 @@ void Texture::CreateRTV(uint32_t index, RTV* pRV, int mipLevel, int arraySize, i // // from Microsoft's D3D12HelloTexture -std::vector Texture::GenerateTexture_Checkerboard(uint Dimension) +std::vector Texture::GenerateTexture_Checkerboard(uint Dimension, bool bUseMidtones /*= false*/) { constexpr UINT TexturePixelSizeInBytes = 4; // byte/px const UINT& TextureWidth = Dimension; @@ -351,16 +278,16 @@ std::vector Texture::GenerateTexture_Checkerboard(uint Dimension) if (i % 2 == j % 2) { - pData[n + 0] = 0x00; // R - pData[n + 1] = 0x00; // G - pData[n + 2] = 0x00; // B + pData[n + 0] = bUseMidtones ? 0x03 : 0x00; // R + pData[n + 1] = bUseMidtones ? 0x03 : 0x00; // G + pData[n + 2] = bUseMidtones ? 0x03 : 0x00; // B pData[n + 3] = 0xff; // A } else { - pData[n + 0] = 0xff; // R - pData[n + 1] = 0xff; // G - pData[n + 2] = 0xff; // B + pData[n + 0] = bUseMidtones ? 0x1F : 0xff; // R + pData[n + 1] = bUseMidtones ? 0x1F : 0xff; // G + pData[n + 2] = bUseMidtones ? 0x1F : 0xff; // B pData[n + 3] = 0xff; // A } } diff --git a/Source/Renderer/Texture.h b/Source/Renderer/Texture.h index deb6f00f..81c363bd 100644 --- a/Source/Renderer/Texture.h +++ b/Source/Renderer/Texture.h @@ -20,6 +20,9 @@ #include "Common.h" +#include + +#include #include namespace D3D12MA { class Allocation; class Allocator; } @@ -29,29 +32,54 @@ class CBV_SRV_UAV; class DSV; class RTV; struct D3D12_SHADER_RESOURCE_VIEW_DESC; +struct Image; struct TextureCreateDesc { TextureCreateDesc(const std::string& name) : TexName(name) {} ID3D12Device* pDevice = nullptr; D3D12MA::Allocator* pAllocator = nullptr; - UploadHeap* pUploadHeap = nullptr; - D3D12_RESOURCE_DESC Desc = {}; + D3D12_RESOURCE_DESC d3d12Desc = {}; D3D12_RESOURCE_STATES ResourceState = D3D12_RESOURCE_STATE_COMMON; const std::string& TexName; - }; class Texture { public: - static std::vector GenerateTexture_Checkerboard(uint Dimension); + struct CubemapUtility + { + // cube face order: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476906(v=vs.85).aspx + //------------------------------------------------------------------------------------------------------ + // 0: RIGHT 1: LEFT + // 2: UP 3: DOWN + // 4: FRONT 5: BACK + //------------------------------------------------------------------------------------------------------ + enum ECubeMapLookDirections + { + CUBEMAP_LOOK_RIGHT = 0, + CUBEMAP_LOOK_LEFT, + CUBEMAP_LOOK_UP, + CUBEMAP_LOOK_DOWN, + CUBEMAP_LOOK_FRONT, + CUBEMAP_LOOK_BACK, + + NUM_CUBEMAP_LOOK_DIRECTIONS + }; +#if 0 + // TODO implement with Lights + static DirectX::XMMATRIX CalculateViewMatrix(ECubeMapLookDirections cubeFace, const vec3& position = vec3::Zero); + inline static DirectX::XMMATRIX CalculateViewMatrix(int face, const vec3& position = vec3::Zero) { return CalculateViewMatrix(static_cast(face), position); } +#endif + }; + static std::vector GenerateTexture_Checkerboard(uint Dimension, bool bUseMidtones = false); Texture() = default; ~Texture() = default; + Texture(const Texture& other); + Texture& operator=(const Texture& other); - bool CreateFromFile(const TextureCreateDesc& desc, const std::string& FilePath); void Create(const TextureCreateDesc& desc, const void* pData = nullptr); void Destroy(); @@ -64,8 +92,11 @@ class Texture inline const ID3D12Resource* GetResource() const { return mpTexture; } inline ID3D12Resource* GetResource() { return mpTexture; } public: + static bool ReadImageFromDisk(const std::string& path, Image& img); private: + friend class VQRenderer; D3D12MA::Allocation* mpAlloc = nullptr; ID3D12Resource* mpTexture = nullptr; + std::atomic bResident = false; }; \ No newline at end of file diff --git a/Source/Scenes/DefaultScene.cpp b/Source/Scenes/DefaultScene.cpp new file mode 100644 index 00000000..38027b57 --- /dev/null +++ b/Source/Scenes/DefaultScene.cpp @@ -0,0 +1,72 @@ +#define NOMINMAX + +#include "Scenes.h" + +#include "../Application/Input.h" + +using namespace DirectX; + +static void Toggle(bool& b) { b = !b; } + + +void DefaultScene::UpdateScene(float dt, FSceneView& SceneView) +{ + assert(pObject); + assert(mIndex_SelectedCamera < mCameras.size()); + + Camera& cam = mCameras[mIndex_SelectedCamera]; + + // handle input + if (mInput.IsKeyTriggered('R')) + { + FCameraParameters params = mSceneRepresentation.Cameras[mIndex_SelectedCamera]; + params.ProjectionParams.ViewportWidth = static_cast(mpWindow->GetWidth() ); + params.ProjectionParams.ViewportHeight = static_cast(mpWindow->GetHeight()); + cam.InitializeCamera(params); + } + + if (mInput.IsKeyTriggered("Space")) Toggle(this->bObjectAnimation); + + Transform* pTF = mpTransforms[pObject->mTransformID]; + constexpr float MOUSE_BUTTON_ROTATION_SPEED_MULTIPLIER = 1.0f; + if (mInput.IsMouseDown(Input::EMouseButtons::MOUSE_BUTTON_LEFT)) pTF->RotateAroundAxisRadians(ZAxis, dt * PI * MOUSE_BUTTON_ROTATION_SPEED_MULTIPLIER); + if (mInput.IsMouseDown(Input::EMouseButtons::MOUSE_BUTTON_RIGHT)) pTF->RotateAroundAxisRadians(YAxis, dt * PI * MOUSE_BUTTON_ROTATION_SPEED_MULTIPLIER); + if (mInput.IsMouseDown(Input::EMouseButtons::MOUSE_BUTTON_MIDDLE)) pTF->RotateAroundAxisRadians(XAxis, dt * PI * MOUSE_BUTTON_ROTATION_SPEED_MULTIPLIER); + + constexpr float DOUBLE_CLICK_MULTIPLIER = 4.0f; + if (mInput.IsMouseDoubleClick(Input::EMouseButtons::MOUSE_BUTTON_LEFT)) pTF->RotateAroundAxisRadians(ZAxis, dt * PI * DOUBLE_CLICK_MULTIPLIER); + if (mInput.IsMouseDoubleClick(Input::EMouseButtons::MOUSE_BUTTON_RIGHT)) pTF->RotateAroundAxisRadians(YAxis, dt * PI * DOUBLE_CLICK_MULTIPLIER); + if (mInput.IsMouseDoubleClick(Input::EMouseButtons::MOUSE_BUTTON_MIDDLE)) pTF->RotateAroundAxisRadians(XAxis, dt * PI * DOUBLE_CLICK_MULTIPLIER); + + constexpr float SCROLL_SCALE_DELTA = 1.1f; + const float CubeScale = pTF->_scale.x; + if (mInput.IsMouseScrollUp()) pTF->SetUniformScale(CubeScale * SCROLL_SCALE_DELTA); + if (mInput.IsMouseScrollDown()) pTF->SetUniformScale(std::max(0.5f, CubeScale / SCROLL_SCALE_DELTA)); + + // update scene data + if (this->bObjectAnimation) + pTF->RotateAroundAxisRadians(YAxis, dt * 0.2f * PI); +} + + +void DefaultScene::InitializeScene() +{ + assert(!mpObjects.empty()); + this->pObject = mpObjects.front(); + this->bObjectAnimation = true; +} + + +void DefaultScene::LoadScene(FSceneRepresentation& scene) +{ +} + +void DefaultScene::UnloadScene() +{ + +} + +void DefaultScene::RenderSceneUI() const +{ +} + diff --git a/Source/Scenes/GeometryUnitTestScene.cpp b/Source/Scenes/GeometryUnitTestScene.cpp new file mode 100644 index 00000000..8df40f70 --- /dev/null +++ b/Source/Scenes/GeometryUnitTestScene.cpp @@ -0,0 +1,27 @@ +#include "Scenes.h" + +void GeometryUnitTestScene::UpdateScene(float dt, FSceneView& SceneView) +{ + +} + + +void GeometryUnitTestScene::InitializeScene() +{ + +} + + +void GeometryUnitTestScene::LoadScene(FSceneRepresentation& scene) +{ +} + +void GeometryUnitTestScene::UnloadScene() +{ + +} + +void GeometryUnitTestScene::RenderSceneUI() const +{ +} + diff --git a/Source/Scenes/Scenes.h b/Source/Scenes/Scenes.h new file mode 100644 index 00000000..75bd579f --- /dev/null +++ b/Source/Scenes/Scenes.h @@ -0,0 +1,58 @@ +// TBA + +#include "../Application/Scene.h" + +#pragma once + +class VQEngine; + +#define DECLARE_SCENE_INTERFACE()\ +protected:\ + void InitializeScene() override;\ + void UpdateScene(float dt, FSceneView& SceneView) override;\ + void LoadScene(FSceneRepresentation& scene) override;\ + void UnloadScene() override;\ + void RenderSceneUI() const override;\ + +#define DECLARE_CTOR(TypeName)\ +public:\ + TypeName(VQEngine& engine\ + , int NumFrameBuffers\ + , const Input& input\ + , const std::unique_ptr& pWin\ + , VQRenderer& renderer\ + )\ + : Scene(engine, NumFrameBuffers, input, pWin, renderer)\ + {}\ + + +class DefaultScene : public Scene +{ + DECLARE_SCENE_INTERFACE() + DECLARE_CTOR(DefaultScene) + +private: + GameObject* pObject = nullptr; + bool bObjectAnimation = false; +}; + +class SponzaScene : public Scene +{ + DECLARE_SCENE_INTERFACE() + + DECLARE_CTOR(SponzaScene) +}; + +class GeometryUnitTestScene : public Scene +{ + DECLARE_SCENE_INTERFACE() + + DECLARE_CTOR(GeometryUnitTestScene) +}; + +class StressTestScene : public Scene +{ + DECLARE_SCENE_INTERFACE() + + DECLARE_CTOR(StressTestScene) +}; \ No newline at end of file diff --git a/Source/Scenes/SponzaScene.cpp b/Source/Scenes/SponzaScene.cpp new file mode 100644 index 00000000..6b8f17e7 --- /dev/null +++ b/Source/Scenes/SponzaScene.cpp @@ -0,0 +1,23 @@ +#include "Scenes.h" + +void SponzaScene::InitializeScene() +{ +} + + +void SponzaScene::UpdateScene(float dt, FSceneView& SceneView) +{ + +} + +void SponzaScene::LoadScene(FSceneRepresentation& scene) +{ +} + +void SponzaScene::UnloadScene() +{ +} + +void SponzaScene::RenderSceneUI() const +{ +} \ No newline at end of file diff --git a/Source/Scenes/StressTestScene.cpp b/Source/Scenes/StressTestScene.cpp new file mode 100644 index 00000000..1e39ba0a --- /dev/null +++ b/Source/Scenes/StressTestScene.cpp @@ -0,0 +1,98 @@ +#define NOMINMAX + +#include "Scenes.h" + +#include "../Application/Input.h" + +using namespace DirectX; + + +void StressTestScene::UpdateScene(float dt, FSceneView& SceneView) +{ + +} + + +void StressTestScene::InitializeScene() +{ + +} + + +void StressTestScene::LoadScene(FSceneRepresentation& scene) +{ + // + // MATERIALS + // + FMaterialRepresentation matRep = {}; + matRep.Alpha = 1.0f; + matRep.DiffuseColor = { 1, 1, 1 }; + matRep.DiffuseMapFilePath = "Procedural/Checkerboard"; + matRep.Name = "Checkerboard"; + scene.Materials.push_back(matRep); + + matRep.DiffuseMapFilePath = "Procedural/Checkerboard_Grayscale"; + matRep.Name = "Checkerboard_Grayscale"; + scene.Materials.push_back(matRep); + + // + // OBJECTS + // + + // small cubes + constexpr int NUM_OBJECTS = 1024; + + constexpr int DIMENSION_X = 16; + constexpr int DIMENSION_Y = 4; + constexpr int DIMENSION_Z = 16; +#if 0 + for (int i = 0; i < NUM_OBJECTS; ++i) +#else + int NumObjects = 0; + constexpr float distance = 5.0f; + for(int x=-DIMENSION_X/2; x