Repository: DragonJoker/RenderGraph Branch: master Commit: 5aebc0c2803e Files: 108 Total size: 996.6 KB Directory structure: gitextract_6fjoabne/ ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── codecov.yml │ └── workflows/ │ └── cmake.yml ├── .gitignore ├── .gitmodules ├── AUTHORS ├── CMakeLists.txt ├── CMakePresets.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── include/ │ └── RenderGraph/ │ ├── Attachment.hpp │ ├── AttachmentTransition.hpp │ ├── BufferData.hpp │ ├── BufferViewData.hpp │ ├── DotExport.hpp │ ├── Exception.hpp │ ├── FrameGraph.hpp │ ├── FrameGraphBase.hpp │ ├── FrameGraphEnums.hpp │ ├── FrameGraphFunctions.hpp │ ├── FrameGraphPrerequisites.hpp │ ├── FrameGraphStructs.hpp │ ├── FramePass.hpp │ ├── FramePassGroup.hpp │ ├── FramePassTimer.hpp │ ├── GraphContext.hpp │ ├── GraphNode.hpp │ ├── GraphVisitor.hpp │ ├── Hash.hpp │ ├── Id.hpp │ ├── ImageData.hpp │ ├── ImageViewData.hpp │ ├── LayerLayoutStatesHandler.hpp │ ├── Log.hpp │ ├── PixelFormat.inl │ ├── RecordContext.hpp │ ├── ResourceHandler.hpp │ ├── RunnableGraph.hpp │ ├── RunnablePass.hpp │ ├── RunnablePasses/ │ │ ├── BufferCopy.hpp │ │ ├── BufferToImageCopy.hpp │ │ ├── ComputePass.hpp │ │ ├── GenerateMipmaps.hpp │ │ ├── ImageBlit.hpp │ │ ├── ImageCopy.hpp │ │ ├── ImageToBufferCopy.hpp │ │ ├── PipelineConfig.hpp │ │ ├── PipelineHolder.hpp │ │ ├── RenderMesh.hpp │ │ ├── RenderMeshConfig.hpp │ │ ├── RenderMeshHolder.hpp │ │ ├── RenderPass.hpp │ │ ├── RenderPassHolder.hpp │ │ ├── RenderQuad.hpp │ │ ├── RenderQuadConfig.hpp │ │ └── RenderQuadHolder.hpp │ ├── Signal.hpp │ ├── Version.hpp.in │ └── WriteDescriptorSet.hpp ├── source/ │ ├── CMakeLists.txt │ └── RenderGraph/ │ ├── Attachment.cpp │ ├── AttachmentTransition.cpp │ ├── BuilderCommon.hpp │ ├── DotExport.cpp │ ├── FrameGraph.cpp │ ├── FrameGraph.natvis │ ├── FrameGraphPrerequisites.cpp │ ├── FramePass.cpp │ ├── FramePassGroup.cpp │ ├── FramePassTimer.cpp │ ├── GraphBuilder.cpp │ ├── GraphBuilder.hpp │ ├── GraphContext.cpp │ ├── GraphNode.cpp │ ├── LayerLayoutStatesHandler.cpp │ ├── Log.cpp │ ├── RecordContext.cpp │ ├── ResourceHandler.cpp │ ├── RunnableGraph.cpp │ ├── RunnablePass.cpp │ └── RunnablePasses/ │ ├── BufferCopy.cpp │ ├── BufferToImageCopy.cpp │ ├── ComputePass.cpp │ ├── GenerateMipmaps.cpp │ ├── ImageBlit.cpp │ ├── ImageCopy.cpp │ ├── ImageToBufferCopy.cpp │ ├── PipelineHolder.cpp │ ├── RenderMesh.cpp │ ├── RenderMeshHolder.cpp │ ├── RenderPass.cpp │ ├── RenderPassHolder.cpp │ ├── RenderQuad.cpp │ └── RenderQuadHolder.cpp ├── test/ │ ├── BaseTest.cpp │ ├── BaseTest.hpp │ ├── CMakeLists.txt │ ├── Common.cpp │ ├── Common.hpp │ ├── TestAttachment.cpp │ ├── TestBases.cpp │ ├── TestRenderGraph.cpp │ ├── TestRenderPass.cpp │ └── TestRunnablePass.cpp └── vcpkg.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 end_of_line = CRLF indent_style = tab indent_size = 4 tab_width = 4 trim_trailing_whitespace = true insert_final_newline = true [*.{c++,cc,cpp,cppm,cu,cuh,cxx,fx,h,h++,hh,hlsl,hpp,hxx,inl,ipp,ixx,tlh,tli}] # Visual C++ Code Style settings cpp_generate_documentation_comments = doxygen_slash_star # Visual C++ Formatting settings cpp_indent_braces = false cpp_indent_multi_line_relative_to = innermost_parenthesis cpp_indent_within_parentheses = indent cpp_indent_preserve_within_parentheses = true cpp_indent_case_contents = true cpp_indent_case_labels = false cpp_indent_case_contents_when_block = true cpp_indent_lambda_braces_when_parameter = true cpp_indent_goto_labels = leftmost_column cpp_indent_preprocessor = leftmost_column cpp_indent_access_specifiers = false cpp_indent_namespace_contents = true cpp_indent_preserve_comments = true cpp_new_line_before_open_brace_namespace = new_line cpp_new_line_before_open_brace_type = new_line cpp_new_line_before_open_brace_function = new_line cpp_new_line_before_open_brace_block = new_line cpp_new_line_before_open_brace_lambda = new_line cpp_new_line_scope_braces_on_separate_lines = true cpp_new_line_close_brace_same_line_empty_type = false cpp_new_line_close_brace_same_line_empty_function = false cpp_new_line_before_catch = true cpp_new_line_before_else = true cpp_new_line_before_while_in_do_while = true cpp_space_before_function_open_parenthesis = remove cpp_space_within_parameter_list_parentheses = true cpp_space_between_empty_parameter_list_parentheses = false cpp_space_after_keywords_in_control_flow_statements = true cpp_space_within_control_flow_statement_parentheses = true cpp_space_before_lambda_open_parenthesis = false cpp_space_within_cast_parentheses = true cpp_space_after_cast_close_parenthesis = false cpp_space_within_expression_parentheses = true cpp_space_before_block_open_brace = true cpp_space_between_empty_braces = false cpp_space_before_initializer_list_open_brace = false cpp_space_within_initializer_list_braces = true cpp_space_preserve_in_initializer_list = false cpp_space_before_open_square_bracket = false cpp_space_within_square_brackets = false cpp_space_before_empty_square_brackets = false cpp_space_between_empty_square_brackets = false cpp_space_group_square_brackets = true cpp_space_within_lambda_brackets = false cpp_space_between_empty_lambda_brackets = false cpp_space_before_comma = false cpp_space_after_comma = true cpp_space_remove_around_member_operators = true cpp_space_before_inheritance_colon = true cpp_space_before_constructor_colon = true cpp_space_remove_before_semicolon = true cpp_space_after_semicolon = true cpp_space_remove_around_unary_operator = true cpp_space_around_binary_operator = insert cpp_space_around_assignment_operator = insert cpp_space_pointer_reference_alignment = center cpp_space_around_ternary_operator = insert cpp_use_unreal_engine_macro_formatting = true cpp_wrap_preserve_blocks = never # Visual C++ Inlcude Cleanup settings cpp_include_cleanup_add_missing_error_tag_type = suggestion cpp_include_cleanup_remove_unused_error_tag_type = dimmed ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/codecov.yml ================================================ codecov: require_ci_to_pass: yes coverage: precision: 2 round: down range: "70...100" status: project: default: target: auto threshold: 100% base: auto patch: default: target: auto threshold: 100% base: auto if_ci_failed: success informational: true parsers: gcov: branch_detection: conditional: yes loop: yes method: no macro: no comment: layout: "reach,diff,flags,files,footer" behavior: default require_changes: no require_head: no require_base: no github_checks: annotations: true ================================================ FILE: .github/workflows/cmake.yml ================================================ name: Build on: push: branches: - master pull_request: branches: - master release: types: - created jobs: build-windows: strategy: matrix: os: [[windows-latest,x64-windows,msvc]] buildType: [Debug] runs-on: ${{ matrix.os[0] }} steps: - uses: actions/checkout@v4 - name: Checkout submodules run: | git submodule update --init -- "CMake" git submodule update --init -- "external/vcpkg" - name: Setup vcpkg uses: lukka/run-vcpkg@v11 with: vcpkgDirectory: '${{ github.workspace }}/external/vcpkg' vcpkgJsonGlob: 'vcpkg.json' - name: Setup OpenCppCoverage id: setup_opencppcoverage run: | choco install OpenCppCoverage -y echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH - name: Create Build Environment run: | cmake -E make_directory ${{runner.workspace}}/build-${{ matrix.buildType }} - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} run: | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.buildType }} -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/external/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/package/rendergraph -DPROJECTS_COVERAGE=ON -DVULKAN_HEADERS_INCLUDE_DIRS=$VCPKG_ROOT/installed/${{matrix.os[1]}}/include --preset ci - name: Build working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | cmake --build . --parallel 2 --config ${{ matrix.buildType }} - name: Build coverage report working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | cmake --build . --target RenderGraphCoverage --config ${{ matrix.buildType }} - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} file: ${{ github.workspace }}/doc/RenderGraphCoverage.xml - name: Prepare package if: github.event_name == 'push' working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | cmake --install . --config ${{ matrix.buildType }} - name: Zip package if: github.event_name == 'push' working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | 7z a $GITHUB_WORKSPACE/RenderGraph-${{ matrix.os[1] }}.zip $GITHUB_WORKSPACE/package/rendergraph/ - name: Upload package if: github.event_name == 'push' uses: actions/upload-artifact@v4 with: name: RenderGraph-${{ matrix.os[1] }}.zip path: ${{ github.workspace }}/RenderGraph-${{ matrix.os[1] }}.zip build-macos: strategy: matrix: os: [[macos-latest,x64-osx,clang]] buildType: [Release] runs-on: ${{ matrix.os[0] }} steps: - uses: actions/checkout@v4 - name: Checkout submodules run: | git submodule update --init -- "CMake" git submodule update --init -- "external/vcpkg" - name: Setup vcpkg uses: lukka/run-vcpkg@v11 with: vcpkgDirectory: '${{ github.workspace }}/external/vcpkg' vcpkgJsonGlob: 'vcpkg.json' - name: Create Build Environment run: | cmake -E make_directory ${{runner.workspace}}/build-${{ matrix.buildType }} - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} run: | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.buildType }} -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/external/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/package/rendergraph -DVULKAN_HEADERS_INCLUDE_DIRS=$VCPKG_ROOT/installed/${{matrix.os[1]}}/include --preset ci - name: Build working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | cmake --build . --parallel 2 --config ${{ matrix.buildType }} - name: Test working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | ctest -C ${{ matrix.buildType }} - name: Prepare package if: github.event_name == 'push' working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | cmake --install . --config ${{ matrix.buildType }} - name: Zip package if: github.event_name == 'push' working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | 7z a $GITHUB_WORKSPACE/RenderGraph-${{ matrix.os[1] }}.zip $GITHUB_WORKSPACE/package/rendergraph/ - name: Upload package if: github.event_name == 'push' uses: actions/upload-artifact@v4 with: name: RenderGraph-${{ matrix.os[1] }}.zip path: ${{ github.workspace }}/RenderGraph-${{ matrix.os[1] }}.zip build-linux-gcc: strategy: matrix: os: [[ubuntu-latest,x64-linux,gcc]] buildType: [Release] runs-on: ${{ matrix.os[0] }} steps: - uses: actions/checkout@v4 - name: Checkout submodules run: | git submodule update --init -- "CMake" git submodule update --init -- "external/vcpkg" - name: Setup vcpkg uses: lukka/run-vcpkg@v11 with: vcpkgDirectory: '${{ github.workspace }}/external/vcpkg' vcpkgJsonGlob: 'vcpkg.json' - name: Create Build Environment run: | cmake -E make_directory ${{runner.workspace}}/build-${{ matrix.buildType }} - name: Configure GCC version shell: bash working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} run: | echo "CC=gcc-12" >> $GITHUB_ENV echo "CXX=g++-12" >> $GITHUB_ENV - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} run: | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.buildType }} -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/external/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/package/rendergraph -DVULKAN_HEADERS_INCLUDE_DIRS=$VCPKG_ROOT/installed/${{matrix.os[1]}}/include --preset ci - name: Build working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | cmake --build . --parallel 2 --config ${{ matrix.buildType }} - name: Test working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | ctest -C ${{ matrix.buildType }} - name: Prepare package if: github.event_name == 'push' working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | cmake --install . --config ${{ matrix.buildType }} - name: Zip package if: github.event_name == 'push' working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | 7z a $GITHUB_WORKSPACE/RenderGraph-${{ matrix.os[1] }}-gcc.zip $GITHUB_WORKSPACE/package/rendergraph/ - name: Upload package if: github.event_name == 'push' uses: actions/upload-artifact@v4 with: name: RenderGraph-${{ matrix.os[1] }}-gcc.zip path: ${{ github.workspace }}/RenderGraph-${{ matrix.os[1] }}-gcc.zip build-linux-clang: strategy: matrix: os: [[ubuntu-latest,x64-linux,clang]] buildType: [Release] runs-on: ${{ matrix.os[0] }} steps: - uses: actions/checkout@v4 - name: Checkout submodules run: | git submodule update --init -- "CMake" git submodule update --init -- "external/vcpkg" - name: Setup vcpkg uses: lukka/run-vcpkg@v11 with: vcpkgDirectory: '${{ github.workspace }}/external/vcpkg' vcpkgJsonGlob: 'vcpkg.json' - name: Create Build Environment run: | cmake -E make_directory ${{runner.workspace}}/build-${{ matrix.buildType }} - name: Configure Warnings as errors (OFF) if: github.event_name == 'push' shell: bash working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} run: | echo "PROJ_WAE=OFF" >> $GITHUB_ENV - name: Configure Warnings as errors (ON) if: github.event_name == 'pull_request' shell: bash working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} run: | echo "PROJ_WAE=ON" >> $GITHUB_ENV - name: Configure Clang version shell: bash working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} run: | echo "CC=clang-16" >> $GITHUB_ENV echo "CXX=clang++-16" >> $GITHUB_ENV - name: Configure CMake shell: bash working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} run: | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.buildType }} -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/external/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/package/rendergraph -DVULKAN_HEADERS_INCLUDE_DIRS=$VCPKG_ROOT/installed/${{matrix.os[1]}}/include --preset ci - name: Build working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | cmake --build . --parallel 2 --config ${{ matrix.buildType }} - name: Test working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | ctest -C ${{ matrix.buildType }} - name: Prepare package if: github.event_name == 'push' working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | cmake --install . --config ${{ matrix.buildType }} - name: Zip package if: github.event_name == 'push' working-directory: ${{runner.workspace}}/build-${{ matrix.buildType }} shell: bash run: | 7z a $GITHUB_WORKSPACE/RenderGraph-${{ matrix.os[1] }}-clang.zip $GITHUB_WORKSPACE/package/rendergraph/ - name: Upload package if: github.event_name == 'push' uses: actions/upload-artifact@v4 with: name: RenderGraph-${{ matrix.os[1] }}-clang.zip path: ${{ github.workspace }}/RenderGraph-${{ matrix.os[1] }}-clang.zip ================================================ FILE: .gitignore ================================================ /.vscode /binaries /build* /setup /doc/coverage /doc/RenderGraphCoverage /doc/RenderGraphCoverage.xml /doc/x86 /doc/x64 /.gitattributes *.csproj *.user *.suo *.gch *.*~ ================================================ FILE: .gitmodules ================================================ [submodule "CMake"] path = CMake url = https://github.com/DragonJoker/CMakeUtils.git [submodule "external/vcpkg"] path = external/vcpkg url = https://github.com/microsoft/vcpkg.git ================================================ FILE: AUTHORS ================================================ Authors of RenderGraph Sylvain Doremus Main author ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required( VERSION 3.10 ) cmake_policy( VERSION 3.10 ) #-------------------------------------------------------------------------------------------------- # Initial configurations #-------------------------------------------------------------------------------------------------- # Set project name, used in folders and in workspace creation set( MAIN_PROJECT_NAME "RenderGraph" ) # Set project version numbers set( VERSION_MAJOR 2 ) set( VERSION_MINOR 1 ) set( VERSION_BUILD 0 ) set( VERSION_YEAR 2025 ) set( _PROJECT_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}" ) set( _PROJECT_SOVERSION "${VERSION_BUILD}" ) # Used to look for external modules if ( NOT CMAKE_MODULE_PATH ) set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMake ${CMAKE_SOURCE_DIR}/CMake/Modules ${CMAKE_SOURCE_DIR}/CMake/Toolchains ) set( CMAKE_TEMPLATES_DIR ${CMAKE_SOURCE_DIR}/CMake/Templates ) endif () set( CMAKE_POSITION_INDEPENDENT_CODE ON ) set( CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo" CACHE STRING "The configuration types" FORCE ) # Experimental Precompiled headers support for GCC include( PCHSupport ) # Declare the project project( ${MAIN_PROJECT_NAME} ) include( Setup ) include( Project ) include( CompilerVersion ) include( UnitTest ) include( CompilationFlags ) include( AStyleUtils ) include( ExternalDependencies ) include( Coverage ) # Organize projects into folders set_property( GLOBAL PROPERTY USE_FOLDERS ON ) #-------------------------------------------------------------------------------------------------- # Adding include dirs to include search path #-------------------------------------------------------------------------------------------------- set( CRG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} ) set( CRG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR} ) set( CRG_EDITORCONFIG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/.editorconfig ) if ( NOT DEFINED CRG_BUILD_STATIC ) option( CRG_BUILD_STATIC "Build as a static library" ON ) endif () if ( NOT DEFINED CRG_BUILD_TESTS ) option( CRG_BUILD_TESTS "Build RenderGraph test applications" OFF ) endif () if ( NOT DEFINED CRG_UNITY_BUILD ) option( CRG_UNITY_BUILD "Build RenderGraph using Unity (Jumbo) build method" OFF ) endif () if ( MSVC OR NOT "${CMAKE_BUILD_TYPE}" STREQUAL "" ) configure_file( ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Version.hpp.in ${CRG_BINARY_DIR}/include/${PROJECT_NAME}/Version.hpp NEWLINE_STYLE LF ) # RenderGraph library project( RenderGraph ) set( ${PROJECT_NAME}_HDR_FILES ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Attachment.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/AttachmentTransition.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/BufferData.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/BufferViewData.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/DotExport.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Exception.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraph.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphBase.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphEnums.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphFunctions.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphPrerequisites.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphStructs.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FramePass.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FramePassGroup.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FramePassTimer.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/GraphContext.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/GraphNode.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/GraphVisitor.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Hash.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Id.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/ImageData.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/ImageViewData.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/LayerLayoutStatesHandler.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Log.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/PixelFormat.inl ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RecordContext.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/ResourceHandler.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnableGraph.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePass.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Signal.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/WriteDescriptorSet.hpp ${CRG_BINARY_DIR}/include/${PROJECT_NAME}/Version.hpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/GraphBuilder.hpp ) set( ${PROJECT_NAME}_SRC_FILES ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/Attachment.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/AttachmentTransition.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/DotExport.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FrameGraph.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FrameGraphPrerequisites.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FramePass.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FramePassGroup.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FramePassTimer.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/GraphBuilder.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/GraphContext.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/GraphNode.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/LayerLayoutStatesHandler.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/Log.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RecordContext.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/ResourceHandler.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnableGraph.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePass.cpp ) set( ${PROJECT_NAME}_NVS_FILES ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FrameGraph.natvis ) source_group( "Header Files" FILES ${${PROJECT_NAME}_HDR_FILES} ) source_group( "Source Files" FILES ${${PROJECT_NAME}_SRC_FILES} ) source_group( "Visualisation Files" FILES ${${PROJECT_NAME}_NVS_FILES} ) set( ${PROJECT_NAME}_FOLDER_HDR_FILES ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/BufferCopy.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/BufferToImageCopy.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/ComputePass.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/GenerateMipmaps.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/ImageBlit.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/ImageCopy.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/ImageToBufferCopy.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/PipelineConfig.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/PipelineHolder.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderPass.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderPassHolder.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderMesh.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderMeshConfig.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderMeshHolder.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderQuad.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderQuadConfig.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderQuadHolder.hpp ) set( ${PROJECT_NAME}_FOLDER_SRC_FILES ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/BufferCopy.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/BufferToImageCopy.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/ComputePass.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/GenerateMipmaps.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/ImageBlit.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/ImageCopy.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/ImageToBufferCopy.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/PipelineHolder.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderPass.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderPassHolder.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderMesh.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderMeshHolder.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderQuad.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderQuadHolder.cpp ) set( ${PROJECT_NAME}_SRC_FILES ${${PROJECT_NAME}_SRC_FILES} ${${PROJECT_NAME}_FOLDER_SRC_FILES} ) set( ${PROJECT_NAME}_HDR_FILES ${${PROJECT_NAME}_HDR_FILES} ${${PROJECT_NAME}_FOLDER_HDR_FILES} ) source_group( "Header Files\\RunnablePasses" FILES ${${PROJECT_NAME}_FOLDER_HDR_FILES} ) source_group( "Source Files\\RunnablePasses" FILES ${${PROJECT_NAME}_FOLDER_SRC_FILES} ) if ( ${CRG_UNITY_BUILD} ) file( GLOB ${PROJECT_NAME}_FOLDER_SRC_FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}.dir/Unity/*.cxx ) source_group( "Source Files\\Unity" FILES ${${PROJECT_NAME}_FOLDER_SRC_FILES} ) endif () if ( CRG_BUILD_STATIC ) add_library( ${PROJECT_NAME} STATIC ${${PROJECT_NAME}_HDR_FILES} ${${PROJECT_NAME}_SRC_FILES} ${${PROJECT_NAME}_NVS_FILES} ) target_compile_definitions( ${PROJECT_NAME} PUBLIC CRG_BUILD_STATIC ) else () add_library( ${PROJECT_NAME} SHARED ${${PROJECT_NAME}_HDR_FILES} ${${PROJECT_NAME}_SRC_FILES} ${${PROJECT_NAME}_NVS_FILES} ) set_target_properties( ${PROJECT_NAME} PROPERTIES VERSION ${_PROJECT_VERSION} SOVERSION ${_PROJECT_SOVERSION} ) if ( WIN32 ) target_link_libraries( ${PROJECT_NAME} PUBLIC Dbghelp ) else () target_link_libraries( ${PROJECT_NAME} PRIVATE dl ) endif () endif () add_library( crg::${PROJECT_NAME} ALIAS ${PROJECT_NAME} ) target_add_coverage_flags( ${PROJECT_NAME} ) target_sources( ${PROJECT_NAME} PRIVATE ${CRG_EDITORCONFIG_FILE} ) target_add_compilation_flags( ${PROJECT_NAME} ) target_compile_options( ${PROJECT_NAME} PUBLIC $<$:-Wno-poison-system-directories> $<$:-Wno-poison-system-directories> ) target_compile_definitions( ${PROJECT_NAME} PUBLIC CRG_VERSION_MAJOR=${VERSION_MAJOR} CRG_VERSION_MINOR=${VERSION_MINOR} CRG_VERSION_BUILD=${VERSION_BUILD} ) target_include_directories( ${PROJECT_NAME} PUBLIC $ $ $ $ ) find_package( VulkanHeaders CONFIG ) target_link_libraries( ${PROJECT_NAME} PRIVATE Vulkan::Headers ) set_target_properties( ${PROJECT_NAME} PROPERTIES CXX_STANDARD 20 FOLDER "${CRG_BASE_DIR}/Core" DEBUG_POSTFIX "d" UNITY_BUILD "${CRG_UNITY_BUILD}" ) install( TARGETS ${PROJECT_NAME} COMPONENT ${PROJECT_NAME} EXPORT ${PROJECT_NAME} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${CRG_BINARY_DIR}/include/${PROJECT_NAME}/Version.hpp DESTINATION include/${PROJECT_NAME} COMPONENT ${PROJECT_NAME} CONFIGURATIONS Release ) target_install_headers( ${PROJECT_NAME} ${CRG_SOURCE_DIR}/include/${PROJECT_NAME} ) install( EXPORT ${PROJECT_NAME} COMPONENT ${PROJECT_NAME} FILE ${PROJECT_NAME}Config.cmake NAMESPACE crg:: DESTINATION share/${PROJECT_NAME} ) include(CMakePackageConfigHelpers) write_basic_package_version_file( ${PROJECT_NAME}ConfigVersion.cmake VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_BUILD} COMPATIBILITY AnyNewerVersion ) if ( CRG_BUILD_TESTS ) add_subdirectory( test ) endif () else() message( SEND_ERROR "Please select a build type (Debug or Release)" ) endif() ================================================ FILE: CMakePresets.json ================================================ { "version": 3, "cmakeMinimumRequired": { "major": 3, "minor": 21, "patch": 0 }, "configurePresets": [ { "name": "default-base", "hidden": true, "displayName": "Default Config", "description": "Default build configuration", "installDir": "${sourceDir}/package/RenderGraph", "cacheVariables": { "CRG_BUILD_STATIC": false, "CRG_BUILD_TESTS": true } }, { "name": "dev-base", "hidden": true, "displayName": "Developer Visual Studio Config", "description": "Developer build configuration using Visual Studio", "inherits": "default-base", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/external/vcpkg/scripts/buildsystems/vcpkg.cmake", "PROJECTS_WARNINGS_AS_ERRORS": true, "VCPKG_MANIFEST_FEATURES": "tests" } }, { "name": "default-msvc", "displayName": "Default Visual Studio Config", "description": "Default build configuration using Visual Studio", "inherits": "default-base", "generator": "Visual Studio 17 2022" }, { "name": "default-ninja-base", "hidden": true, "displayName": "Default Ninja Config", "description": "Default build configuration using Ninja", "inherits": "default-base", "generator": "Ninja" }, { "name": "default-ninja-debug", "displayName": "Default Ninja Config, Debug", "description": "Default build configuration using Ninja, Debug", "inherits": "default-ninja-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "default-ninja-release", "displayName": "Default Ninja Config, Release", "description": "Default build configuration using Ninja, Release", "inherits": "default-ninja-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } }, { "name": "dev-msvc", "displayName": "Developer Visual Studio Config", "description": "Developer build configuration using Visual Studio", "inherits": "dev-base", "generator": "Visual Studio 17 2022" }, { "name": "dev-ninja-base", "hidden": true, "displayName": "Developer Ninja Config", "description": "Developer build configuration using Ninja", "inherits": "dev-base", "generator": "Ninja" }, { "name": "dev-ninja-debug", "displayName": "Developer Ninja Config, Debug", "description": "Developer build configuration using Ninja, Debug", "inherits": "dev-ninja-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "dev-ninja-release", "displayName": "Developer Ninja Config, Release", "description": "Developer build configuration using Ninja, Release", "inherits": "dev-ninja-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } }, { "name": "dev-mingw-base", "hidden": true, "displayName": "Developer MinGW Config", "description": "Developer build configuration using Ninja, on MinGW", "generator": "Ninja", "inherits": "default-base", "cacheVariables": { "PROJECTS_WARNINGS_AS_ERRORS": true, "CRG_BUILD_TESTS": true } }, { "name": "dev-mingw-debug", "displayName": "Developer MinGW Config, Debug", "description": "Developer build configuration using Ninja, on MinGW, Debug", "inherits": "dev-mingw-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "dev-mingw-release", "displayName": "Developer MinGW Config, Release", "description": "Developer build configuration using Ninja, on MinGW, Release", "inherits": "dev-mingw-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } }, { "name": "ci", "installDir": "${sourceDir}/package/Castor3D", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/external/vcpkg/scripts/buildsystems/vcpkg.cmake", "CRG_BUILD_STATIC": false, "CRG_UNITY_BUILD": true, "CRG_BUILD_TESTS": true, "PROJECTS_WARNINGS_AS_ERRORS": true, "VCPKG_MANIFEST_FEATURES": "tests" } } ] } ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dragonjoker59@hotmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Sylvain Doremus Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

Vcpkg Version Build status

# RenderGraph This library owes to be used to handle Vulkan render passes and image transitions smoothly. It allows the user to register its render passes, along with their attachments (input, sampled, colour, depth stencil...), and will generate a runnable graph from that data. ## Current status - The user can register its passes and their attachments. - The runnable graph is generated, and image layout transitions are handled. - The runnable graph commands can be recorded and submitted to a queue. - Handling of "variants" (optional passes, or paths of a single pass that are triggered by specific conditions). ## Building RenderGraph uses CMake. The only dependency is Vulkan-Headers, and the CMake variable holding its folder is VULKAN_HEADERS_INCLUDE_DIRS, which you need to set on command line, or by editing the CMakeCache.txt. ================================================ FILE: include/RenderGraph/Attachment.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "BufferViewData.hpp" #include "ImageViewData.hpp" #include #ifdef None #undef None #endif namespace crg { static constexpr uint32_t InvalidBindingId = std::numeric_limits< uint32_t >::max(); struct SamplerDesc { FilterMode magFilter{ FilterMode::eNearest }; FilterMode minFilter{ FilterMode::eNearest }; MipmapMode mipmapMode{ MipmapMode::eNearest }; WrapMode addressModeU{ WrapMode::eClampToEdge }; WrapMode addressModeV{ WrapMode::eClampToEdge }; WrapMode addressModeW{ WrapMode::eClampToEdge }; float mipLodBias{ 0.0f }; float minLod{ -500.0f }; float maxLod{ 500.0f }; explicit constexpr SamplerDesc( FilterMode magFilter = FilterMode::eNearest , FilterMode minFilter = FilterMode::eNearest , MipmapMode mipmapMode = MipmapMode::eNearest , WrapMode addressModeU = WrapMode::eClampToEdge , WrapMode addressModeV = WrapMode::eClampToEdge , WrapMode addressModeW = WrapMode::eClampToEdge , float mipLodBias = 0.0f , float minLod = -500.0f , float maxLod = 500.0f ) : magFilter{ magFilter } , minFilter{ minFilter } , mipmapMode{ mipmapMode } , addressModeU{ addressModeU } , addressModeV{ addressModeV } , addressModeW{ addressModeW } , mipLodBias{ mipLodBias } , minLod{ minLod } , maxLod{ maxLod } { } private: friend bool operator==( SamplerDesc const & lhs, SamplerDesc const & rhs ) = default; }; /** *\brief * An image attachment. */ struct ImageAttachment { friend struct Attachment; friend struct FramePass; /** *\brief * The flags qualifying the attachment. */ using FlagKind = uint16_t; enum class Flag : FlagKind { None = 0x00, Sampled = 0x01 << 0, Storage = 0x01 << 1, Transfer = 0x01 << 2, Depth = 0x01 << 3, Stencil = 0x01 << 4, StencilClearing = 0x01 << 5, StencilInput = 0x01 << 6, StencilOutput = 0x01 << 7, Transition = 0x01 << 8, DepthStencil = Depth | Stencil, StencilInOut = StencilInput | StencilOutput, }; /** *\name * Getters. */ /**@{*/ CRG_API ImageViewId view( uint32_t index = 0u )const; CRG_API ImageLayout getImageLayout( bool separateDepthStencilLayouts , bool isInput , bool isOutput )const; CRG_API AccessFlags getAccessMask( bool isInput , bool isOutput )const; CRG_API PipelineStageFlags getPipelineStageFlags( bool isCompute )const; FlagKind getFlags()const { return flags; } FlagKind getFormatFlags()const { return FlagKind( flags & FlagKind( Flag::DepthStencil ) ); } uint32_t getViewCount()const { return uint32_t( views.size() ); } bool hasFlag( Flag flag )const { return Flag( flags & FlagKind( flag ) ) == flag; } bool isSampledView()const { return hasFlag( Flag::Sampled ); } bool isStorageView()const { return hasFlag( Flag::Storage ); } bool isTransferView()const { return hasFlag( Flag::Transfer ); } bool isTransitionView()const { return hasFlag( Flag::Transition ); } bool isDepthTarget()const { return hasFlag( Flag::Depth ) && !isTransitionView(); } bool isStencilTarget()const { return hasFlag( Flag::Stencil ) && !isTransitionView(); } bool isDepthStencilTarget()const { return isDepthTarget() && isStencilTarget(); } bool isColourTarget()const { return !isSampledView() && !isTransitionView() && !isStorageView() && !isTransferView() && !isDepthTarget() && !isStencilTarget(); } bool isStencilClearingTarget()const { return hasFlag( Flag::StencilClearing ); } bool isStencilInputTarget()const { return hasFlag( Flag::StencilInput ); } bool isStencilOutputTarget()const { return hasFlag( Flag::StencilOutput ); } /**@}*/ public: ImageViewIdArray views{}; AttachmentLoadOp loadOp{}; AttachmentStoreOp storeOp{}; AttachmentLoadOp stencilLoadOp{}; AttachmentStoreOp stencilStoreOp{}; ClearValue clearValue{}; PipelineColorBlendAttachmentState blendState = DefaultBlendState; ImageLayout wantedLayout{}; FlagKind flags{}; private: ImageAttachment() = default; CRG_API explicit ImageAttachment( ImageViewIdArray view ); CRG_API ImageAttachment( FlagKind flags , ImageViewIdArray views , AttachmentLoadOp loadOp , AttachmentStoreOp storeOp , AttachmentLoadOp stencilLoadOp , AttachmentStoreOp stencilStoreOp , ClearValue clearValue , PipelineColorBlendAttachmentState blendState , ImageLayout wantedLayout ); friend bool operator==( ImageAttachment const & lhs , ImageAttachment const & rhs )noexcept { return lhs.flags == rhs.flags && lhs.views == rhs.views && lhs.loadOp == rhs.loadOp && lhs.storeOp == rhs.storeOp && lhs.stencilLoadOp == rhs.stencilLoadOp && lhs.stencilStoreOp == rhs.stencilStoreOp && lhs.clearValue == rhs.clearValue && lhs.blendState == rhs.blendState; } }; /** *\brief * A buffer (uniform or storage) attachment. */ struct BufferAttachment { friend struct Attachment; friend struct FramePass; /** *\brief * The flags qualifying a buffer attachment. */ using FlagKind = uint16_t; enum class Flag : FlagKind { None = 0x00, Uniform = 0x01 << 0, Storage = 0x01 << 1, Transfer = 0x01 << 2, View = 0x01 << 3, Transition = 0x01 << 4, UniformView = Uniform | View, StorageView = Storage | View, TransitionView = Transition | View, }; CRG_API BufferViewId buffer( uint32_t index = 0u )const; CRG_API AccessFlags getAccessMask( bool isInput , bool isOutput )const; CRG_API PipelineStageFlags getPipelineStageFlags( bool isCompute )const; CRG_API uint32_t getBufferCount()const; FlagKind getFlags()const { return flags; } FlagKind getFormatFlags()const { return FlagKind( flags & FlagKind( Flag::View ) ); } bool hasFlag( Flag flag )const { return Flag( flags & FlagKind( flag ) ) == flag; } bool isUniform()const { return hasFlag( Flag::Uniform ); } bool isStorage()const { return hasFlag( Flag::Storage ); } bool isTransfer()const { return hasFlag( Flag::Transfer ); } bool isTransition()const { return hasFlag( Flag::Transition ); } bool isView()const { return hasFlag( Flag::View ); } bool isUniformView()const { return isUniform() && isView(); } bool isStorageView()const { return isStorage() && isView(); } bool isTransitionView()const { return isTransition() && isView(); } public: BufferViewIdArray buffers; FlagKind flags{}; AccessState wantedAccess{}; private: BufferAttachment() = default; CRG_API explicit BufferAttachment( BufferViewIdArray view ); CRG_API BufferAttachment( FlagKind flags , BufferViewIdArray views , AccessState access = {} ); friend bool operator==( BufferAttachment const & lhs , BufferAttachment const & rhs )noexcept { return lhs.flags == rhs.flags && lhs.buffers == rhs.buffers; } }; /** *\brief * An attachment to a pass. */ struct Attachment { friend struct FramePass; friend class FrameGraph; class Token { friend struct Attachment; friend struct FramePass; private: Token() noexcept = default; }; struct Source { Source( Attachment const * parent , FramePass const * pass , ImageAttachment const & imgAttach ) : parent{ parent } , pass{ pass } , imageAttach{ &imgAttach } { } Source( Attachment const * parent , FramePass const * pass , BufferAttachment const & bufAttach ) : parent{ parent } , pass{ pass } , bufferAttach{ &bufAttach } { } explicit Source( AttachmentPtr sourceAttach ) : pass{ sourceAttach->pass } , imageAttach{ sourceAttach->isImage() ? &sourceAttach->imageAttach : nullptr } , bufferAttach{ sourceAttach->isBuffer() ? &sourceAttach->bufferAttach : nullptr } , attach{ std::move( sourceAttach ) } { } Attachment const * parent{}; FramePass const * pass{}; ImageAttachment const * imageAttach{}; BufferAttachment const * bufferAttach{}; AttachmentPtr attach; }; /** *\brief * The flags qualifying an Attachment. */ using FlagKind = uint16_t; enum class Flag : FlagKind { None = 0x00, Input = 0x01 << 0, Output = 0x01 << 1, Image = 0x01 << 2, Buffer = 0x01 << 3, NoTransition = 0x01 << 4, Clearable = 0x01 << 5, InOut = Input | Output, }; CRG_API Attachment( Attachment const & rhs ); CRG_API Attachment & operator=( Attachment const & rhs ); Attachment( Attachment && rhs )noexcept = default; Attachment & operator=( Attachment && rhs )noexcept = default; /** *\name * Getters. */ /**@{*/ CRG_API uint32_t getViewCount()const; CRG_API uint32_t getBufferCount()const; CRG_API ImageViewId view( uint32_t index = 0u )const; CRG_API BufferViewId buffer( uint32_t index = 0u )const; CRG_API ImageLayout getImageLayout( bool separateDepthStencilLayouts )const; CRG_API AccessFlags getAccessMask()const; CRG_API PipelineStageFlags getPipelineStageFlags( bool isCompute )const; CRG_API Attachment const * getSource( uint32_t index )const; FlagKind getFlags()const { return flags; } bool hasFlag( Flag flag )const { return Flag( flags & FlagKind( flag ) ) == flag; } bool isNoTransition()const { return hasFlag( Flag::NoTransition ); } bool isInput()const { return hasFlag( Flag::Input ); } bool isOutput()const { return hasFlag( Flag::Output ); } bool isInOut()const { return isInput() && isOutput(); } bool isImage()const { return hasFlag( Flag::Image ); } bool isBuffer()const { return hasFlag( Flag::Buffer ); } bool isClearable()const { return hasFlag( Flag::Clearable ); } bool isUniformBuffer()const { return isBuffer() && bufferAttach.isUniform(); } bool isUniformBufferView()const { return isBuffer() && bufferAttach.isUniformView(); } bool isStorageBuffer()const { return isBuffer() && bufferAttach.isStorage(); } bool isTransferBuffer()const { return isBuffer() && bufferAttach.isTransfer(); } bool isTransferInputBuffer()const { return isInput() && isTransferBuffer(); } bool isTransferOutputBuffer()const { return isOutput() && isTransferBuffer(); } bool isClearableBuffer()const { return isBuffer() && isOutput() && isClearable(); } bool isClearableImage()const { return isImage() && isOutput() && isClearable(); } bool isStorageBufferView()const { return isBuffer() && bufferAttach.isStorageView(); } bool isTransitionBuffer()const { return isBuffer() && bufferAttach.isTransition(); } bool isTransitionBufferView()const { return isBuffer() && bufferAttach.isTransitionView(); } bool isBufferView()const { return isBuffer() && bufferAttach.isView(); } bool isSampledImageView()const { return isImage() && imageAttach.isSampledView(); } bool isStorageImageView()const { return isImage() && imageAttach.isStorageView(); } bool isTransferImageView()const { return isImage() && imageAttach.isTransferView(); } bool isTransitionImageView()const { return isImage() && imageAttach.isTransitionView(); } bool isDepthImageTarget()const { return isImage() && imageAttach.isDepthTarget(); } bool isStencilImageTarget()const { return isImage() && imageAttach.isStencilTarget(); } bool isColourImageTarget()const { return !isSampledImageView() && !isTransitionImageView() && !isStorageImageView() && !isTransferImageView() && !isDepthImageTarget() && !isStencilImageTarget(); } bool isColourInputImageTarget()const { return isInput() && isColourImageTarget(); } bool isColourOutputImageTarget()const { return isOutput() && isColourImageTarget(); } bool isColourInOutImageTarget()const { return isInput() && isOutput() && isColourImageTarget(); } bool isDepthInputImageTarget()const { return isInput() && isDepthImageTarget(); } bool isDepthOutputImageTarget()const { return isOutput() && isDepthImageTarget(); } bool isDepthInOutImageTarget()const { return isInput() && isOutput() && isDepthImageTarget(); } bool isStencilClearingImageTarget()const { return isImage() && imageAttach.isStencilClearingTarget(); } bool isStencilInputImageTarget()const { return isImage() && imageAttach.isStencilInputTarget(); } bool isStencilOutputImageTarget()const { return isImage() && imageAttach.isStencilOutputTarget(); } bool isStencilInOutImageTarget()const { return isStencilInputImageTarget() && isStencilOutputImageTarget(); } bool isDepthStencilInputImageTarget()const { return isDepthInputImageTarget() && isStencilInputImageTarget(); } bool isDepthStencilOutputImageTarget()const { return isDepthOutputImageTarget() && isStencilOutputImageTarget(); } bool isDepthStencilInOutImageTarget()const { return isDepthInOutImageTarget() && isStencilInOutImageTarget(); } bool isTransferInputImageView()const { return isInput() && isTransferImageView(); } bool isTransferOutputImageView()const { return isOutput() && isTransferImageView(); } bool isStorageInputImageView()const { return isInput() && isStorageImageView(); } bool isStorageOutputImageView()const { return isOutput() && isStorageImageView(); } BufferSubresourceRange const & getBufferRange()const { return getSubresourceRange( bufferAttach.buffer() ); } ClearValue const & getClearValue()const { return imageAttach.clearValue; } AttachmentLoadOp getLoadOp()const { return imageAttach.loadOp; } AttachmentLoadOp getStencilLoadOp()const { return imageAttach.stencilLoadOp; } AttachmentStoreOp getStoreOp()const { return imageAttach.storeOp; } AttachmentStoreOp getStencilStoreOp()const { return imageAttach.stencilStoreOp; } PipelineColorBlendAttachmentState getBlendState()const { return imageAttach.blendState; } /**@}*/ /** *\brief * Creates a default empty attachment. */ static Attachment createDefault( ImageViewIdArray views ) { return Attachment{ std::move( views ) }; } static Attachment createDefault( BufferViewIdArray views ) { return Attachment{ std::move( views ) }; } static Attachment createDefault( ImageViewId view ) { return createDefault( ImageViewIdArray{ view } ); } static Attachment createDefault( BufferViewId view ) { return createDefault( BufferViewIdArray{ view } ); } /** *\name * Members. */ /**@[*/ FramePass const * pass{}; std::string name{}; ImageAttachment imageAttach{}; BufferAttachment bufferAttach{}; std::vector< Source > source{}; FlagKind flags{}; /**@}*/ CRG_API Attachment( ImageViewId view , Attachment const & origin ); CRG_API Attachment( BufferViewId view , Attachment const & origin ); CRG_API explicit Attachment( ImageViewIdArray view ); CRG_API explicit Attachment( BufferViewIdArray view ); Attachment( FlagKind flags , std::string name , FramePass const * pass , ImageAttachment attach , Token token ); Attachment( FlagKind flags , std::string name , FramePass const * pass , BufferAttachment attach , Token token ); Attachment( FlagKind flags , FramePass const & pass , std::string name , ImageAttachment::FlagKind imageFlags , ImageViewIdArray views , AttachmentLoadOp loadOp , AttachmentStoreOp storeOp , AttachmentLoadOp stencilLoadOp , AttachmentStoreOp stencilStoreOp , ClearValue clearValue , PipelineColorBlendAttachmentState blendState , ImageLayout wantedLayout , Token token ); Attachment( FlagKind flags , FramePass const & pass , std::string name , BufferAttachment::FlagKind bufferFlags , BufferViewIdArray views , AccessState wantedAccess , Token token ); private: void initSources(); friend bool operator==( Attachment const & lhs , Attachment const & rhs ) { return lhs.pass == rhs.pass && lhs.flags == rhs.flags && lhs.imageAttach == rhs.imageAttach && lhs.bufferAttach == rhs.bufferAttach; } }; } ================================================ FILE: include/RenderGraph/AttachmentTransition.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "Attachment.hpp" #include namespace crg { template< typename DataT > struct DataTransitionT { DataTransitionT( DataT data, Attachment outputAttach, Attachment inputAttach )noexcept : data{ std::move( data ) } , outputAttach{ std::move( outputAttach ) } , inputAttach{ std::move( inputAttach ) } { } DataT data; Attachment outputAttach; Attachment inputAttach; private: friend bool operator==( DataTransitionT const & lhs, DataTransitionT const & rhs ) { return match( lhs.data, rhs.data ) && lhs.outputAttach == rhs.outputAttach && lhs.inputAttach == rhs.inputAttach; } }; struct AttachmentTransitions { ImageTransitionArray imageTransitions; BufferTransitionArray bufferTransitions; }; AttachmentTransitions mergeIdenticalTransitions( AttachmentTransitions value ); } ================================================ FILE: include/RenderGraph/BufferData.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "Id.hpp" namespace crg { /** *\brief * Basic buffer data, from which buffers will be created. */ struct BufferData { std::string name; BufferCreateInfo info; explicit BufferData( std::string name = {} , BufferCreateFlags flags = {} , DeviceSize size = {} , BufferUsageFlags usage = {} , MemoryPropertyFlags memory = MemoryPropertyFlags::eDeviceLocal ) : name{ std::move( name ) } , info{ flags, size, usage, memory } { } private: friend bool operator==( BufferData const & lhs, BufferData const & rhs ) = default; }; } ================================================ FILE: include/RenderGraph/BufferViewData.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "Id.hpp" namespace crg { /** *\brief * Basic buffer view data, from which views will be created. */ struct BufferViewData { std::string name; BufferId buffer; BufferViewCreateInfo info; BufferViewIdArray source{}; explicit BufferViewData( std::string name = {} , BufferId buffer = BufferId{} , BufferSubresourceRange subresourceRange = {} , PixelFormat format = PixelFormat::eUNDEFINED ) : name{ std::move( name ) } , buffer{ std::move( buffer ) } , info{ format, subresourceRange } { } private: friend bool operator==( BufferViewData const & lhs, BufferViewData const & rhs ) { return lhs.buffer == rhs.buffer && lhs.info == rhs.info; } }; struct VertexBuffer { explicit VertexBuffer( BufferViewId pbuffer = BufferViewId{} , VkVertexInputAttributeDescriptionArray pvertexAttribs = {} , VkVertexInputBindingDescriptionArray pvertexBindings = {} ) : buffer{ std::move( pbuffer ) } , vertexAttribs{ std::move( pvertexAttribs ) } , vertexBindings{ std::move( pvertexBindings ) } { doUpdateState(); } VertexBuffer( VertexBuffer const & rhs ) : buffer{ rhs.buffer } , vertexAttribs{ rhs.vertexAttribs } , vertexBindings{ rhs.vertexBindings } { doUpdateState(); } VertexBuffer( VertexBuffer && rhs )noexcept : buffer{ std::move( rhs.buffer ) } , vertexAttribs{ std::move( rhs.vertexAttribs ) } , vertexBindings{ std::move( rhs.vertexBindings ) } { doUpdateState(); } VertexBuffer & operator=( VertexBuffer const & rhs ) { buffer = rhs.buffer; vertexAttribs = rhs.vertexAttribs; vertexBindings = rhs.vertexBindings; doUpdateState(); return *this; } VertexBuffer & operator=( VertexBuffer && rhs )noexcept { buffer = std::move( rhs.buffer ); vertexAttribs = std::move( rhs.vertexAttribs ); vertexBindings = std::move( rhs.vertexBindings ); doUpdateState(); return *this; } BufferViewId buffer; VkVertexInputAttributeDescriptionArray vertexAttribs; VkVertexInputBindingDescriptionArray vertexBindings; VkPipelineVertexInputStateCreateInfo inputState{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, nullptr, {}, {}, {}, {}, {} }; private: void doUpdateState() { inputState.vertexAttributeDescriptionCount = uint32_t( vertexAttribs.size() ); inputState.pVertexAttributeDescriptions = vertexAttribs.data(); inputState.vertexBindingDescriptionCount = uint32_t( vertexBindings.size() ); inputState.pVertexBindingDescriptions = vertexBindings.data(); } }; struct IndexBuffer { explicit IndexBuffer( BufferViewId pbuffer = BufferViewId{} ) : buffer{ std::move( pbuffer ) } { } BufferViewId buffer; private: friend bool operator==( IndexBuffer const & lhs, IndexBuffer const & rhs ) = default; }; struct IndirectBuffer { explicit IndirectBuffer( BufferViewId pbuffer , uint32_t pstride ) : buffer{ std::move( pbuffer ) } , stride{ pstride } { } BufferViewId buffer; uint32_t stride; private: friend bool operator==( IndirectBuffer const & lhs, IndirectBuffer const & rhs ) = default; }; template<> struct DefaultValueGetterT< VertexBuffer > { static VertexBuffer get() { VertexBuffer const result{}; return result; } }; template<> struct DefaultValueGetterT< IndexBuffer > { static IndexBuffer get() { IndexBuffer const result{ BufferViewId{} }; return result; } }; template<> struct DefaultValueGetterT< IndirectBuffer > { static IndirectBuffer get() { IndirectBuffer const result{ BufferViewId{}, 0u }; return result; } }; } ================================================ FILE: include/RenderGraph/DotExport.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/FrameGraphPrerequisites.hpp" #pragma warning( push ) #pragma warning( disable: 4365 ) #pragma warning( disable: 5262 ) #include #include #include #include #pragma warning( pop ) namespace crg::dot { struct Config { bool withColours{}; bool withIds{}; bool withGroups{}; bool splitGroups{}; std::string toRemove{}; }; using DisplayResult = std::map< std::string, std::stringstream, std::less<> >; CRG_API DisplayResult displayPasses( RunnableGraph const & value , Config const & config ); CRG_API DisplayResult displayTransitions( RunnableGraph const & value , Config const & config ); CRG_API void displayPasses( std::ostream & stream , RunnableGraph const & value , Config const & config ); CRG_API void displayTransitions( std::ostream & stream , RunnableGraph const & value , Config const & config ); } ================================================ FILE: include/RenderGraph/Exception.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/FrameGraphPrerequisites.hpp" #include namespace crg { class Exception : public std::exception { public: Exception( std::string const & text , std::string const & file , int line ) : text{ file + ":" + std::to_string( line ) + " - " + text } { } char const * what()const noexcept override { return text.c_str(); } private: std::string text; }; #define CRG_Exception( text )\ throw crg::Exception{ text, __FILE__, __LINE__ } } ================================================ FILE: include/RenderGraph/FrameGraph.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "Attachment.hpp" #include "BufferData.hpp" #include "BufferViewData.hpp" #include "FramePassGroup.hpp" #include "GraphNode.hpp" #include "ImageData.hpp" #include "ImageViewData.hpp" #include "RecordContext.hpp" #include #include namespace crg { class FrameGraph { friend class RunnableGraph; public: /** *\name * Construction/Destruction. */ /**@{*/ FrameGraph( FrameGraph const & ) = delete; FrameGraph & operator=( FrameGraph const & ) = delete; FrameGraph & operator=( FrameGraph && )noexcept = delete; FrameGraph( FrameGraph && )noexcept = default; ~FrameGraph()noexcept = default; CRG_API explicit FrameGraph( ResourceHandler & handler , std::string name = "FrameGraph" ); /**@}*/ /** *\name * Resource creation. */ /**@{*/ CRG_API BufferId createBuffer( BufferData const & img ); CRG_API BufferViewId createView( BufferViewData const & view ); CRG_API ImageId createImage( ImageData const & img ); CRG_API ImageViewId createView( ImageViewData const & view ); /**@}*/ /** *\name * Views merging. */ /**@{*/ /** *\brief * Creates a view which represents the given views merging. */ CRG_API ImageViewId mergeViews( ImageViewIdArray const & views , bool mergeMipLevels = true , bool mergeArrayLayers = true ); /** *\brief * Creates a view which represents the given views merging. */ CRG_API BufferViewId mergeViews( BufferViewIdArray const & views ); /**@}*/ /** *\name * Attachments merging. */ /**@{*/ /** *\brief * Creates a view which represents the given views merging. */ CRG_API Attachment const * mergeAttachments( AttachmentArray const & attachments , bool mergeMipLevels = true , bool mergeArrayLayers = true ); /**@}*/ /** *\name * Passes and groups. */ /**@{*/ CRG_API FramePass & createPass( std::string const & name , RunnablePassCreator runnableCreator ); CRG_API FramePassGroup & createPassGroup( std::string const & name ); /**@}*/ /** *\name * Compilation. */ /**@{*/ CRG_API RunnableGraphPtr compile( GraphContext & context ); /**@}*/ /** *\name * Dependencies. */ /**@[*/ void addDependency( FrameGraph const & pgraph ) { m_depends.push_back( &pgraph ); } /**@}*/ /** *\name * Getters. */ /**@{*/ CRG_API LayoutState getFinalLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range )const; CRG_API LayoutState getFinalLayoutState( ImageViewId view , uint32_t passIndex = 0u )const; CRG_API AccessState const & getFinalAccessState( BufferId buffer , BufferSubresourceRange const & range )const; CRG_API AccessState const & getFinalAccessState( BufferViewId view , uint32_t passIndex = 0u )const; CRG_API void addInput( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range , LayoutState const & outputLayout ); CRG_API void addInput( ImageViewId view , LayoutState const & outputLayout ); CRG_API LayoutState getInputLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range )const; CRG_API LayoutState getInputLayoutState( ImageViewId view )const; CRG_API void addOutput( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range , LayoutState const & outputLayout ); CRG_API void addOutput( ImageViewId view , LayoutState const & outputLayout ); CRG_API LayoutState getOutputLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range )const; CRG_API LayoutState getOutputLayoutState( ImageViewId view )const; CRG_API LayerLayoutStatesMap const & getOutputLayoutStates()const; ResourceHandler & getHandler()noexcept { return m_handler; } std::string const & getName()const noexcept { return m_name; } FrameGraphArray const & getDependencies()const noexcept { return m_depends; } RecordContext const & getFinalStates()const noexcept { return m_finalState; } FramePassGroup & getDefaultGroup()const noexcept { return *m_defaultGroup; } /**@}*/ private: void registerFinalState( RecordContext const & context ); private: ResourceHandler & m_handler; std::string m_name; FramePassGroupPtr m_defaultGroup; std::set< BufferId > m_buffers; std::set< BufferViewId > m_bufferViews; std::set< ImageId > m_images; std::set< ImageViewId > m_imageViews; std::map< std::string, ImageViewId, std::less<> > m_attachViews; RecordContext m_finalState; FrameGraphArray m_depends; LayerLayoutStatesHandler m_inputs; LayerLayoutStatesHandler m_outputs; std::unordered_map< size_t, AttachmentPtr > m_mergedAttachments; }; } ================================================ FILE: include/RenderGraph/FrameGraphBase.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #pragma warning( push ) #pragma warning( disable: 4365 ) #pragma warning( disable: 4865 ) #pragma warning( disable: 5262 ) #include #include #include #include #include #include #include #include #include #include #pragma warning( pop ) #if !defined( _WIN32 ) || defined( CRG_BUILD_STATIC ) # define CRG_API #else # if defined( RenderGraph_EXPORTS ) # define CRG_API __declspec( dllexport ) # else # define CRG_API __declspec( dllimport ) # endif #endif namespace crg { struct Attachment; struct AttachmentTransitions; struct BufferData; struct BufferViewData; struct FramePass; struct FramePassGroup; struct GraphContext; struct GraphNode; struct ImageData; struct ImageViewData; struct IndexBuffer; struct IndirectBuffer; struct LayoutState; struct PipelineState; struct RootNode; struct SamplerDesc; struct SemaphoreWait; struct Texcoord; struct VertexBuffer; struct WriteDescriptorSet; class ContextResourcesCache; class Exception; class Fence; class FrameGraph; class FramePassTimer; class GraphVisitor; class ImageCopy; class PipelinePass; class RecordContext; class RenderPass; class RenderQuad; class ResourceHandler; class ResourcesCache; class RunnableGraph; class RunnablePass; template< typename DataT > struct Id; template< typename DataT > struct DataTransitionT; template< typename DataT > using DataTransitionArrayT = std::vector< DataTransitionT< DataT > >; template< typename VkTypeT > struct ContextObjectT; template< typename TypeT > struct DefaultValueGetterT; template< typename TypeT > struct RawTyperT; using BufferId = Id< BufferData >; using BufferViewId = Id< BufferViewData >; using ImageId = Id< ImageData >; using ImageViewId = Id< ImageViewData >; using AccessState = PipelineState; using DependencyCache = std::unordered_map< size_t, bool >; using PassDependencyCache = std::unordered_map< FramePass const *, DependencyCache >; using DeviceSize = VkDeviceSize; using AttachmentPtr = std::unique_ptr< Attachment >; using FramePassPtr = std::unique_ptr< FramePass >; using FramePassGroupPtr = std::unique_ptr< FramePassGroup >; using GraphNodePtr = std::unique_ptr< GraphNode >; using RunnableGraphPtr = std::unique_ptr< RunnableGraph >; using RunnablePassPtr = std::unique_ptr< RunnablePass >; using VertexBufferPtr = std::unique_ptr< VertexBuffer >; using GraphAdjacentNode = GraphNode *; using ConstGraphAdjacentNode = GraphNode const *; /** *\brief * The transition between two states of an image view. */ using ImageTransition = DataTransitionT< ImageViewId >; using ImageTransitionArray = DataTransitionArrayT< ImageViewId >; /** *\brief * The transition between two states of a buffer. */ using BufferTransition = DataTransitionT< BufferViewId >; using BufferTransitionArray = DataTransitionArrayT< BufferViewId >; using AttachmentArray = std::vector< Attachment const * >; using BufferViewIdArray = std::vector< BufferViewId >; using FramePassPtrArray = std::vector< FramePassPtr >; using FramePassGroupPtrArray = std::vector< FramePassGroupPtr >; using FrameGraphArray = std::vector< FrameGraph const * >; using FramePassArray = std::vector< FramePass const * >; using GraphAdjacentNodeArray = std::vector< GraphAdjacentNode >; using ConstGraphAdjacentNodeArray = std::vector< ConstGraphAdjacentNode >; using GraphNodePtrArray = std::vector< GraphNodePtr >; using WriteDescriptorSetArray = std::vector< WriteDescriptorSet >; using AttachmentsNodeMap = std::map< ConstGraphAdjacentNode, AttachmentTransitions >; using BufferMemoryMap = std::map< BufferId, std::pair< VkBuffer, VkDeviceMemory > >; using BufferViewMap = std::map< BufferViewId, VkBufferView >; using ImageMemoryMap = std::map< ImageId, std::pair< VkImage, VkDeviceMemory > >; using ImageViewMap = std::map< ImageViewId, VkImageView >; using ImageViewIdArray = std::vector< ImageViewId >; using SemaphoreWaitArray = std::vector< SemaphoreWait >; template< typename DataT > using IdDataOwnerCont = std::map< Id< DataT >, std::unique_ptr< DataT > >; using BufferIdDataOwnerCont = IdDataOwnerCont< BufferData >; using BufferViewIdDataOwnerCont = IdDataOwnerCont< BufferViewData >; using ImageIdDataOwnerCont = IdDataOwnerCont< ImageData >; using ImageViewIdDataOwnerCont = IdDataOwnerCont< ImageViewData >; using VkAttachmentDescriptionArray = std::vector< VkAttachmentDescription >; using VkAttachmentReferenceArray = std::vector< VkAttachmentReference >; using VkBufferArray = std::vector< VkBuffer >; using VkBufferViewArray = std::vector< VkBufferView >; using VkDescriptorBufferInfoArray = std::vector< VkDescriptorBufferInfo >; using VkDescriptorImageInfoArray = std::vector< VkDescriptorImageInfo >; using VkDescriptorSetLayoutBindingArray = std::vector< VkDescriptorSetLayoutBinding >; using VkDescriptorPoolSizeArray = std::vector< VkDescriptorPoolSize >; using VkImageViewArray = std::vector< VkImageView >; using VkPipelineColorBlendAttachmentStateArray = std::vector< VkPipelineColorBlendAttachmentState >; using VkPipelineShaderStageCreateInfoArray = std::vector< VkPipelineShaderStageCreateInfo >; using VkPushConstantRangeArray = std::vector< VkPushConstantRange >; using VkScissorArray = std::vector< VkRect2D >; using VkSubpassDependencyArray = std::vector< VkSubpassDependency >; using VkVertexInputAttributeDescriptionArray = std::vector< VkVertexInputAttributeDescription >; using VkVertexInputBindingDescriptionArray = std::vector< VkVertexInputBindingDescription >; using VkViewportArray = std::vector< VkViewport >; using VkWriteDescriptorSetArray = std::vector< VkWriteDescriptorSet >; using MipLayoutStates = std::map< uint32_t, LayoutState >; using LayerLayoutStates = std::map< uint32_t, MipLayoutStates >; using LayoutStateMap = std::unordered_map< uint32_t, LayerLayoutStates >; using LayerLayoutStatesMap = std::map< uint32_t, LayerLayoutStates >; using AccessStateMap = std::unordered_map< uint32_t, AccessState >; using ViewsLayout = LayoutStateMap; using BuffersLayout = AccessStateMap; using ViewsLayoutPtr = std::unique_ptr< ViewsLayout >; using BuffersLayoutPtr = std::unique_ptr< BuffersLayout >; using ViewsLayouts = std::vector< ViewsLayoutPtr >; using BuffersLayouts = std::vector< BuffersLayoutPtr >; using ViewLayoutIterators = std::map< uint32_t, ViewsLayouts::iterator >; using BufferLayoutIterators = std::map< uint32_t, BuffersLayouts::iterator >; } ================================================ FILE: include/RenderGraph/FrameGraphEnums.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "FrameGraphBase.hpp" #define CRG_MakeFlags( FlagBits )\ constexpr FlagBits operator|( FlagBits lhs, FlagBits rhs ) { return FlagBits( std::underlying_type_t< FlagBits >( lhs ) | std::underlying_type_t< FlagBits >( rhs ) ); }\ constexpr FlagBits operator&( FlagBits lhs, FlagBits rhs ) { return FlagBits( std::underlying_type_t< FlagBits >( lhs ) & std::underlying_type_t< FlagBits >( rhs ) ); }\ constexpr FlagBits operator^( FlagBits lhs, FlagBits rhs ) { return FlagBits( std::underlying_type_t< FlagBits >( lhs ) ^ std::underlying_type_t< FlagBits >( rhs ) ); }\ constexpr FlagBits & operator|=( FlagBits & lhs, FlagBits rhs ) { return lhs = lhs | rhs; }\ constexpr FlagBits & operator&=( FlagBits & lhs, FlagBits rhs ) { return lhs = lhs & rhs; }\ constexpr FlagBits & operator^=( FlagBits & lhs, FlagBits rhs ) { return lhs = lhs ^ rhs; }\ constexpr bool checkFlag( FlagBits lhs, FlagBits rhs ) { return ( lhs & rhs ) == rhs; } namespace crg { enum class PixelFormat : int32_t { #define RGPF_ENUM_VALUE( name, value, components, alpha, colour, depth, stencil, compressed ) e##name = value, #define RGPF_ENUM_NON_VALUE( name, value ) e##name = value, #include "PixelFormat.inl" }; enum class ImageType : int32_t { e1D = 0, e2D = 1, e3D = 2, }; enum class SampleCount : int32_t { e1 = 0x00000001, e2 = 0x00000002, e4 = 0x00000004, e8 = 0x00000008, e16 = 0x00000010, e32 = 0x00000020, e64 = 0x00000040, }; enum class ImageTiling : int32_t { eOptimal = 0, eLinear = 1, eDRMFormatModifier = 1000158000, }; enum class ImageViewType : int32_t { e1D = 0, e2D = 1, e3D = 2, eCube = 3, e1DArray = 4, e2DArray = 5, eCubeArray = 6, }; enum class ImageLayout : int32_t { eUndefined = 0, eGeneral = 1, eColorAttachment = 2, eDepthStencilAttachment = 3, eDepthStencilReadOnly = 4, eShaderReadOnly = 5, eTransferSrc = 6, eTransferDst = 7, ePreinitialized = 8, eDepthReadOnlyStencilAttachment = 1000117000, eDepthAttachmentStencilReadOnly = 1000117001, eDepthAttachment = 1000241000, eDepthReadOnly = 1000241001, eStencilAttachment = 1000241002, eStencilReadOnly = 1000241003, eReadOnly = 1000314000, eAttachment = 1000314001, eRenderingLocalRead = 1000232000, ePresentSrc = 1000001002, eVideoDecodeDst = 1000024000, eVideoDecodeSrc = 1000024001, eVideoDecodeDpb = 1000024002, eSharedPresent = 1000111000, eFragmentDensityMap = 1000218000, eFragmentShadingRateAttachment = 1000164003, eVideoEncodeDst = 1000299000, eVideoEncodeSrc = 1000299001, eVideoEncodeDpb = 1000299002, eAttachmentFeedbackLoop = 1000339000, eVideoEncodeQuantizationMap = 1000553000, }; enum class FilterMode : int32_t { eNearest, eLinear, }; enum class MipmapMode : int32_t { eNearest, eLinear, }; enum class WrapMode : int32_t { eRepeat, eMirroredRepeat, eClampToEdge, eClampToBorder, eMirrorClampToEdge, }; enum class AttachmentLoadOp : int32_t { eLoad = 0, eClear = 1, eDontCare = 2, eNone = 1000400000, }; enum class AttachmentStoreOp : int32_t { eStore = 0, eDontCare = 1, eNone = 1000301000, }; enum class BlendFactor : int32_t { eZero = 0, eOne = 1, eSrcColor = 2, eOneMinusSrcColor = 3, eDstColor = 4, eOneMinusDstColor = 5, eSrcAlpha = 6, eOneMinusSrcAlpha = 7, eDstAlpha = 8, eOneMinusDstAlpha = 9, eConstantColor = 10, eOneMinusConstantColor = 11, eConstantAlpha = 12, eOneMinusConstantAlpha = 13, eSrcAlphaSaturate = 14, eSrc1Color = 15, eOneMinusSrc1Color = 16, eSrc1Alpha = 17, eOneMinusSrc1Alpha = 18, }; enum class BlendOp : int32_t { eAdd = 0, eSubtract = 1, eReverse_subtract = 2, eMin = 3, eMax = 4, eZero = 1000148000, eSrc = 1000148001, eDst = 1000148002, eSrcOver = 1000148003, eDstOver = 1000148004, eSrcIn = 1000148005, eDstIn = 1000148006, eSrcOut = 1000148007, eDstOut = 1000148008, eSrcAtop = 1000148009, eDstAtop = 1000148010, eXor = 1000148011, eMultiply = 1000148012, eScreen = 1000148013, eOverlay = 1000148014, eDarken = 1000148015, eLighten = 1000148016, eColordodge = 1000148017, eColorburn = 1000148018, eHardlight = 1000148019, eSoftlight = 1000148020, eDifference = 1000148021, eExclusion = 1000148022, eInvert = 1000148023, eInvertRGB = 1000148024, eLineardodge = 1000148025, eLinearburn = 1000148026, eVividlight = 1000148027, eLinearlight = 1000148028, ePinlight = 1000148029, eHardmix = 1000148030, eHSLHue = 1000148031, eHSLSaturation = 1000148032, eHSLColor = 1000148033, eHSLLuminosity = 1000148034, ePlus = 1000148035, ePlusClamped = 1000148036, ePlusClamped_alpha = 1000148037, ePlusDarker = 1000148038, eMinus = 1000148039, eMinus_clamped = 1000148040, eContrast = 1000148041, eInvertOVG = 1000148042, eRed = 1000148043, eGreen = 1000148044, eBlue = 1000148045, }; enum class BufferCreateFlags : int32_t { eNone = 0x00000000, eSparseBinding = 0x00000001, eSparseResidency = 0x00000002, eSparseAliased = 0x00000004, eProtected = 0x00000008, eDeviceAddressCaptureReplay = 0x00000010, eDescriptorBufferCaptureReplay = 0x00000020, eVideoProfileIndependent = 0x00000040, }; CRG_MakeFlags( BufferCreateFlags ) enum class BufferUsageFlags : int32_t { eNone = 0x00000000, eTransferSrc = 0x00000001, eTransferDst = 0x00000002, eUniformTexelBuffer = 0x00000004, eStorageTexelBuffer = 0x00000008, eUniformBuffer = 0x00000010, eStorageBuffer = 0x00000020, eIndexBuffer = 0x00000040, eVertexBuffer = 0x00000080, eIndirectBuffer = 0x00000100, eShaderDeviceAddress = 0x00020000, eVideoDecodeSrc = 0x00002000, eVideoDecodeDst = 0x00004000, eTransformFeedbackBuffer = 0x00000800, eTransformFeedbackCounterBuffer = 0x00001000, eConditionalRendering = 0x00000200, eAccelerationStructureBuildInputReadOnly = 0x00080000, eAccelerationStructureStorage = 0x00100000, eShaderBindingTable = 0x00000400, eVideoEncodeDst = 0x00008000, eVideoEncodeSrc = 0x00010000, eSamplerDescriptorBuffer = 0x00200000, eResourceDescriptorBuffer = 0x00400000, ePushDescriptorsDescriptorBuffer = 0x04000000, eMicromapBuildInputReadOnly = 0x00800000, eMicromapStorage = 0x01000000, eTileMemory = 0x08000000, }; CRG_MakeFlags( BufferUsageFlags ) enum class MemoryPropertyFlags : int32_t { eNone = 0x0000000, eDeviceLocal = 0x00000001, eHostVisible = 0x00000002, eHostCoherent = 0x00000004, eHostCached = 0x00000008, eLazilyAllocated = 0x00000010, eProtected = 0x00000020, eDeviceCoherent = 0x00000040, eDeviceUncached = 0x00000080, eRdmaCapable = 0x00000100, }; CRG_MakeFlags( MemoryPropertyFlags ) enum class ImageCreateFlags : int32_t { eNone = 0, eSparseBinding = 0x00000001, eSparseResidency = 0x00000002, eSparseAliased = 0x00000004, eMutableFormat = 0x00000008, eCubeCompatible = 0x00000010, eAlias = 0x00000400, eSplitInstanceBindRegions = 0x00000040, e2DArrayCompatible = 0x00000020, eBlockTexelViewCompatible = 0x00000080, eExtendedUsage = 0x00000100, eProtected = 0x00000800, eDisjoint = 0x00000200, eCornerSampled = 0x00002000, eSampleLocationsCompatibleDepth = 0x00001000, eSubsampled = 0x00004000, eDescriptorBufferCaptureReplay = 0x00010000, eMultisampledRenderToSingleSampled = 0x00040000, e2DViewCompatible = 0x00020000, eVideoProfileIndependent = 0x00100000, eFragmentDensityMapOffset = 0x00008000, }; CRG_MakeFlags( ImageCreateFlags ) enum class ImageUsageFlags : int32_t { eNone = 0, eTransferSrc = 0x00000001, eTransferDst = 0x00000002, eSampled = 0x00000004, eStorage = 0x00000008, eColorAttachment = 0x00000010, eDepthStencilAttachment = 0x00000020, eTransientAttachment = 0x00000040, eInputAttachment = 0x00000080, eHostTransfer = 0x00400000, eVideoDecodeDst = 0x00000400, eVideoDecodeSrc = 0x00000800, eVideoDecodeDpb = 0x00001000, eFragmentDensityMap = 0x00000200, eFragmentShadingRateAttachment = 0x00000100, eVideoEncodeDst = 0x00002000, eVideoEncodeSrc = 0x00004000, eVideoEncodeDpb = 0x00008000, eAttachmentFeedbackLoop = 0x00080000, eInvocationMask = 0x00040000, eSampleWeight = 0x00100000, eSampleBlockMatch = 0x00200000, eTileMemory = 0x08000000, eVideoEncodeQuantizationDeltaMap = 0x02000000, eVideoEncodeEmphasisMap = 0x04000000, }; CRG_MakeFlags( ImageUsageFlags ) enum class ImageViewCreateFlags : int32_t { eNone = 0, eFragmentDensityMapDynamic = 0x00000001, eDescriptorBufferCaptureReplay = 0x00000004, eFragmentDensityMapDeferred = 0x00000002, }; CRG_MakeFlags( ImageViewCreateFlags ) enum class ImageAspectFlags : int32_t { eNone = 0, eColor = 0x00000001, eDepth = 0x00000002, eStencil = 0x00000004, eDepthStencil = eDepth | eStencil, eMetadata = 0x00000008, ePlane0 = 0x00000010, ePlane1 = 0x00000020, ePlane2 = 0x00000040, eMemoryPlane0 = 0x00000080, eMemoryPlane1 = 0x00000100, eMemoryPlane2 = 0x00000200, eMemoryPlane3 = 0x00000400, }; CRG_MakeFlags( ImageAspectFlags ) enum class PipelineStageFlags : int32_t { eNone = 0, eTopOfPipe = 0x00000001, eDrawIndirect = 0x00000002, eVertexInput = 0x00000004, eVertexShader = 0x00000008, eTessellationControlShader = 0x00000010, eTessellationEvaluationShader = 0x00000020, eGeometryShader = 0x00000040, eFragmentShader = 0x00000080, eEarlyFragmentTests = 0x00000100, eLateFragmentTests = 0x00000200, eColorAttachmentOutput = 0x00000400, eComputeShader = 0x00000800, eTransfer = 0x00001000, eBottomOfPipe = 0x00002000, eHost = 0x00004000, eAllGraphics = 0x00008000, eAllCommands = 0x00010000, eTransformFeedback = 0x01000000, eConditionalRendering = 0x00040000, eAccelerationStructureBuild = 0x02000000, eRayTracingShader = 0x00200000, eFragmentDensityProcess = 0x00800000, eFragmentShadingRateAttachment = 0x00400000, eTaskShader = 0x00080000, eMeshShader = 0x00100000, eCommandPreprocess = 0x00020000, }; CRG_MakeFlags( PipelineStageFlags ) enum class AccessFlags : int32_t { eNone = 0, eIndirectCommandRead = 0x00000001, eIndexRead = 0x00000002, eVertexAttributeRead = 0x00000004, eUniformRead = 0x00000008, eInputAttachmentRead = 0x00000010, eShaderRead = 0x00000020, eShaderWrite = 0x00000040, eColorAttachmentRead = 0x00000080, eColorAttachmentWrite = 0x00000100, eDepthStencilAttachmentRead = 0x00000200, eDepthStencilAttachmentWrite = 0x00000400, eTransferRead = 0x00000800, eTransferWrite = 0x00001000, eHostRead = 0x00002000, eHostWrite = 0x00004000, eMemoryRead = 0x00008000, eMemoryWrite = 0x00010000, eTransformFeedbackWrite = 0x02000000, eTransformFeedbackCounterRead = 0x04000000, eTransformFeedbackCounterWrite = 0x08000000, eConditionalRenderingRead = 0x00100000, eColorAttachmentReadNonCoherent = 0x00080000, eAccelerationStructureRead = 0x00200000, eAccelerationStructureWrite = 0x00400000, eFragmentDensityMapRead = 0x01000000, eFragmentShadingRateAttachmentRead = 0x00800000, eCommandPreprocessRead = 0x00020000, eCommandPreprocessWrite = 0x00040000, }; CRG_MakeFlags( AccessFlags ) enum class ColorComponentFlags : int32_t { eNone = 0, eR = 0x00000001, eG = 0x00000002, eB = 0x00000004, eA = 0x00000008, }; CRG_MakeFlags( ColorComponentFlags ) } ================================================ FILE: include/RenderGraph/FrameGraphFunctions.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "FrameGraphStructs.hpp" #include namespace crg { CRG_API std::string_view getName( PixelFormat v ); CRG_API std::string_view getName( FilterMode v ); CRG_API std::string_view getName( MipmapMode v ); CRG_API std::string_view getName( WrapMode v ); CRG_API ImageCreateFlags getImageCreateFlags( ImageId const & image )noexcept; CRG_API ImageCreateFlags getImageCreateFlags( ImageViewId const & image )noexcept; CRG_API Extent3D const & getExtent( ImageId const & image )noexcept; CRG_API Extent3D const & getExtent( ImageViewId const & image )noexcept; CRG_API DeviceSize getSize( BufferId const & image )noexcept; CRG_API DeviceSize getSize( BufferViewId const & image )noexcept; CRG_API Extent3D getMipExtent( ImageViewId const & image )noexcept; CRG_API PixelFormat getFormat( ImageId const & image )noexcept; CRG_API PixelFormat getFormat( ImageViewId const & image )noexcept; CRG_API ImageType getImageType( ImageId const & image )noexcept; CRG_API ImageType getImageType( ImageViewId const & image )noexcept; CRG_API ImageViewType getImageViewType( ImageViewId const & image )noexcept; CRG_API uint32_t getMipLevels( ImageId const & image )noexcept; CRG_API uint32_t getMipLevels( ImageViewId const & image )noexcept; CRG_API uint32_t getArrayLayers( ImageId const & image )noexcept; CRG_API uint32_t getArrayLayers( ImageViewId const & image )noexcept; CRG_API ImageAspectFlags getAspectFlags( ImageViewId const & image )noexcept; CRG_API ImageSubresourceRange const & getSubresourceRange( ImageViewId const & image )noexcept; CRG_API BufferSubresourceRange const & getSubresourceRange( BufferViewId const & buffer )noexcept; CRG_API AccessFlags getAccessMask( ImageLayout layout )noexcept; CRG_API PipelineStageFlags getStageMask( ImageLayout layout )noexcept; CRG_API PipelineState getPipelineState( PipelineStageFlags flags )noexcept; CRG_API LayoutState makeLayoutState( ImageLayout layout )noexcept; CRG_API ImageAspectFlags getAspectMask( PixelFormat format )noexcept; CRG_API LayoutState const & addSubresourceRangeLayout( LayerLayoutStates & ranges , ImageSubresourceRange const & range , LayoutState const & newLayout ); CRG_API LayoutState getSubresourceRangeLayout( LayerLayoutStates const & ranges , ImageSubresourceRange const & range ); CRG_API ImageSubresourceRange getVirtualRange( ImageId const & image , ImageViewType viewType , ImageSubresourceRange const & range )noexcept; CRG_API bool match( ImageViewId const & lhs, ImageViewId const & rhs )noexcept; CRG_API bool match( BufferViewId const & lhs, BufferViewId const & rhs )noexcept; CRG_API ImageViewId const & resolveView( ImageViewId const & view , uint32_t passIndex ); CRG_API BufferViewId const & resolveView( BufferViewId const & view , uint32_t passIndex ); CRG_API void convert( SemaphoreWaitArray const & toWait , std::vector< VkSemaphore > & semaphores , std::vector< VkPipelineStageFlags > & dstStageMasks ); CRG_API std::vector< VkClearValue > convert( std::vector< ClearValue > const & v ); CRG_API VkQueryPool createQueryPool( GraphContext & context , std::string const & name , uint32_t passesCount ); CRG_API ClearColorValue getClearColorValue( ClearValue const & v ); CRG_API ClearDepthStencilValue getClearDepthStencilValue( ClearValue const & v ); constexpr VkFormat convert( PixelFormat v )noexcept { return VkFormat( v ); } constexpr PixelFormat convert( VkFormat v )noexcept { return PixelFormat( v ); } constexpr bool isDepthFormat( PixelFormat fmt )noexcept { return fmt == PixelFormat::eD16_UNORM || fmt == PixelFormat::eX8_D24_UNORM || fmt == PixelFormat::eD32_SFLOAT || fmt == PixelFormat::eD16_UNORM_S8_UINT || fmt == PixelFormat::eD24_UNORM_S8_UINT || fmt == PixelFormat::eD32_SFLOAT_S8_UINT; } constexpr bool isStencilFormat( PixelFormat fmt )noexcept { return fmt == PixelFormat::eS8_UINT || fmt == PixelFormat::eD16_UNORM_S8_UINT || fmt == PixelFormat::eD24_UNORM_S8_UINT || fmt == PixelFormat::eD32_SFLOAT_S8_UINT; } constexpr bool isColourFormat( PixelFormat fmt )noexcept { return !isDepthFormat( fmt ) && !isStencilFormat( fmt ); } constexpr bool isDepthStencilFormat( PixelFormat fmt )noexcept { return isDepthFormat( fmt ) && isStencilFormat( fmt ); } constexpr bool isDepthOrStencilFormat( PixelFormat fmt )noexcept { return isDepthFormat( fmt ) || isStencilFormat( fmt ); } constexpr VkImageType convert( ImageType v )noexcept { return VkImageType( v ); } constexpr ImageType convert( VkImageType v )noexcept { return ImageType( v ); } constexpr VkSampleCountFlagBits convert( SampleCount v )noexcept { return VkSampleCountFlagBits( v ); } constexpr SampleCount convert( VkSampleCountFlagBits v )noexcept { return SampleCount( v ); } constexpr VkImageTiling convert( ImageTiling v )noexcept { return VkImageTiling( v ); } constexpr ImageTiling convert( VkImageTiling v )noexcept { return ImageTiling( v ); } constexpr VkImageViewType convert( ImageViewType v )noexcept { return VkImageViewType( v ); } constexpr ImageViewType convert( VkImageViewType v )noexcept { return ImageViewType( v ); } constexpr VkImageLayout convert( ImageLayout v )noexcept { return VkImageLayout( v ); } constexpr ImageLayout convert( VkImageLayout v )noexcept { return ImageLayout( v ); } constexpr VkFilter convert( FilterMode v )noexcept { return VkFilter( v ); } constexpr FilterMode convert( VkFilter v )noexcept { return FilterMode( v ); } constexpr VkSamplerMipmapMode convert( MipmapMode v )noexcept { return VkSamplerMipmapMode( v ); } constexpr MipmapMode convert( VkSamplerMipmapMode v )noexcept { return MipmapMode( v ); } constexpr VkSamplerAddressMode convert( WrapMode v )noexcept { return VkSamplerAddressMode( v ); } constexpr WrapMode convert( VkSamplerAddressMode v )noexcept { return WrapMode( v ); } constexpr VkAttachmentLoadOp convert( AttachmentLoadOp v )noexcept { return VkAttachmentLoadOp( v ); } constexpr AttachmentLoadOp convert( VkAttachmentLoadOp v )noexcept { return AttachmentLoadOp( v ); } constexpr VkBlendFactor convert( BlendFactor v )noexcept { return VkBlendFactor( v ); } constexpr BlendFactor convert( VkBlendFactor v )noexcept { return BlendFactor( v ); } constexpr VkBlendOp convert( BlendOp v )noexcept { return VkBlendOp( v ); } constexpr BlendOp convert( VkBlendOp v )noexcept { return BlendOp( v ); } constexpr VkAttachmentStoreOp convert( AttachmentStoreOp v )noexcept { return VkAttachmentStoreOp( v ); } constexpr AttachmentStoreOp convert( VkAttachmentStoreOp v )noexcept { return AttachmentStoreOp( v ); } constexpr VkBufferCreateFlags getBufferCreateFlags( BufferCreateFlags v )noexcept { return VkBufferCreateFlags( v ); } constexpr BufferCreateFlags getBufferCreateFlags( VkBufferCreateFlags v )noexcept { return BufferCreateFlags( v ); } constexpr VkBufferUsageFlags getBufferUsageFlags( BufferUsageFlags v )noexcept { return VkBufferUsageFlags( v ); } constexpr BufferUsageFlags getBufferUsageFlags( VkBufferUsageFlags v )noexcept { return BufferUsageFlags( v ); } constexpr VkMemoryPropertyFlags getMemoryPropertyFlags( MemoryPropertyFlags v )noexcept { return VkMemoryPropertyFlags( v ); } constexpr MemoryPropertyFlags getMemoryPropertyFlags( VkMemoryPropertyFlags v )noexcept { return MemoryPropertyFlags( v ); } constexpr VkImageCreateFlags getImageCreateFlags( ImageCreateFlags v )noexcept { return VkImageCreateFlags( v ); } constexpr ImageCreateFlags getImageCreateFlags( VkImageCreateFlags v )noexcept { return ImageCreateFlags( v ); } constexpr VkImageUsageFlags getImageUsageFlags( ImageUsageFlags v )noexcept { return VkImageUsageFlags( v ); } constexpr ImageUsageFlags getImageUsageFlags( VkImageUsageFlags v )noexcept { return ImageUsageFlags( v ); } constexpr VkImageViewCreateFlags getImageViewCreateFlags( ImageViewCreateFlags v )noexcept { return VkImageViewCreateFlags( v ); } constexpr ImageViewCreateFlags getImageViewCreateFlags( VkImageViewCreateFlags v )noexcept { return ImageViewCreateFlags( v ); } constexpr VkImageAspectFlags getImageAspectFlags( ImageAspectFlags v )noexcept { return VkImageAspectFlags( v ); } constexpr ImageAspectFlags getImageAspectFlags( VkImageAspectFlags v )noexcept { return ImageAspectFlags( v ); } constexpr VkPipelineStageFlags getPipelineStageFlags( PipelineStageFlags v )noexcept { return VkPipelineStageFlags( v ); } constexpr PipelineStageFlags getPipelineStageFlags( VkPipelineStageFlags v )noexcept { return PipelineStageFlags( v ); } constexpr VkAccessFlags getAccessFlags( AccessFlags v )noexcept { return VkAccessFlags( v ); } constexpr AccessFlags getAccessFlags( VkAccessFlags v )noexcept { return AccessFlags( v ); } constexpr VkColorComponentFlags getColorComponentFlags( ColorComponentFlags v )noexcept { return VkColorComponentFlags( v ); } constexpr ColorComponentFlags getColorComponentFlags( VkColorComponentFlags v )noexcept { return ColorComponentFlags( v ); } constexpr VkExtent2D convert( Extent2D const & v )noexcept { return std::bit_cast< VkExtent2D >( v ); } constexpr Extent2D convert( VkExtent2D const & v )noexcept { return std::bit_cast< Extent2D >( v ); } constexpr VkOffset2D convert( Offset2D const & v )noexcept { return std::bit_cast< VkOffset2D >( v ); } constexpr Offset2D convert( VkOffset2D const & v )noexcept { return std::bit_cast< Offset2D >( v ); } constexpr VkRect2D convert( Rect2D const & v )noexcept { return std::bit_cast< VkRect2D >( v ); } constexpr Rect2D convert( VkRect2D const & v )noexcept { return std::bit_cast< Rect2D >( v ); } constexpr VkExtent3D convert( Extent3D const & v )noexcept { return std::bit_cast< VkExtent3D >( v ); } constexpr Extent3D convert( VkExtent3D const & v )noexcept { return std::bit_cast< Extent3D >( v ); } constexpr VkOffset3D convert( Offset3D const & v )noexcept { return std::bit_cast< VkOffset3D >( v ); } constexpr Offset3D convert( VkOffset3D const & v )noexcept { return std::bit_cast< Offset3D >( v ); } constexpr VkImageSubresourceRange convert( ImageSubresourceRange const & v )noexcept { return std::bit_cast< VkImageSubresourceRange >( v ); } constexpr ImageSubresourceRange convert( VkImageSubresourceRange const & v )noexcept { return std::bit_cast< ImageSubresourceRange >( v ); } constexpr VkPipelineColorBlendAttachmentState convert( PipelineColorBlendAttachmentState const & v )noexcept { return std::bit_cast< VkPipelineColorBlendAttachmentState >( v ); } constexpr PipelineColorBlendAttachmentState convert( VkPipelineColorBlendAttachmentState const & v )noexcept { return std::bit_cast< PipelineColorBlendAttachmentState >( v ); } constexpr VkClearDepthStencilValue convert( ClearDepthStencilValue const & v )noexcept { return std::bit_cast< VkClearDepthStencilValue >( v ); } constexpr VkClearColorValue convert( ClearColorValue const & v )noexcept { if ( v.isFloat32() ) { VkClearColorValue result; result.float32[0] = v.float32()[0]; result.float32[1] = v.float32()[1]; result.float32[2] = v.float32()[2]; result.float32[3] = v.float32()[3]; return result; } if ( v.isInt32() ) { VkClearColorValue result; result.int32[0] = v.int32()[0]; result.int32[1] = v.int32()[1]; result.int32[2] = v.int32()[2]; result.int32[3] = v.int32()[3]; return result; } VkClearColorValue result; result.uint32[0] = v.uint32()[0]; result.uint32[1] = v.uint32()[1]; result.uint32[2] = v.uint32()[2]; result.uint32[3] = v.uint32()[3]; return result; } constexpr VkClearValue convert( ClearValue const & v )noexcept { if ( v.isColor() ) { VkClearValue result; result.color = convert( v.color() ); return result; } VkClearValue result; result.depthStencil = convert( v.depthStencil() ); return result; } constexpr VkBufferViewCreateInfo convert( BufferViewCreateInfo const & v )noexcept { return VkBufferViewCreateInfo{ VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, nullptr , 0, VkBuffer{}, convert( v.format ) , v.subresourceRange.offset, v.subresourceRange.size }; } constexpr BufferViewCreateInfo convert( VkBufferViewCreateInfo const & v )noexcept { return BufferViewCreateInfo{ convert( v.format ) , { v.offset, v.range } }; } constexpr VkBufferCreateInfo convert( BufferCreateInfo const & v )noexcept { return VkBufferCreateInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr , getBufferCreateFlags( v.flags ), v.size , getBufferUsageFlags( v.usage ) , VK_SHARING_MODE_EXCLUSIVE, 0u, nullptr }; } constexpr BufferCreateInfo convert( VkBufferCreateInfo const & v )noexcept { return BufferCreateInfo{ getBufferCreateFlags( v.flags ) , v.size , getBufferUsageFlags( v.usage ) }; } constexpr VkImageViewCreateInfo convert( ImageViewCreateInfo const & v )noexcept { return VkImageViewCreateInfo{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, nullptr , getImageViewCreateFlags( v.flags ), VkImage{}, convert( v.viewType ), convert( v.format ) , { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY } , convert( v.subresourceRange ) }; } constexpr ImageViewCreateInfo convert( VkImageViewCreateInfo const & v )noexcept { return ImageViewCreateInfo{ getImageViewCreateFlags( v.flags ), convert( v.viewType ), convert( v.format ) , convert( v.subresourceRange ) }; } constexpr VkImageCreateInfo convert( ImageCreateInfo const & v )noexcept { return VkImageCreateInfo{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, nullptr , getImageCreateFlags( v.flags ), convert( v.imageType ), convert( v.format ) , convert( v.extent ), v.mipLevels, v.arrayLayers, convert( v.samples ) , convert( v.tiling ), getImageUsageFlags( v.usage ) , VK_SHARING_MODE_EXCLUSIVE, 0u, nullptr , VK_IMAGE_LAYOUT_UNDEFINED }; } constexpr ImageCreateInfo convert( VkImageCreateInfo const & v )noexcept { return ImageCreateInfo{ getImageCreateFlags( v.flags ), convert( v.imageType ), convert( v.format ) , convert( v.extent ), v.mipLevels, v.arrayLayers, convert( v.samples ) , convert( v.tiling ), getImageUsageFlags( v.usage ) }; } inline VkImageSubresourceLayers getSubresourceLayers( ImageSubresourceRange const & range , uint32_t layerCount ) { return VkImageSubresourceLayers{ getImageAspectFlags( range.aspectMask ) , range.baseMipLevel , range.baseArrayLayer , layerCount }; } inline VkImageSubresourceLayers getSubresourceLayers( ImageSubresourceRange const & range ) { return getSubresourceLayers( range, range.layerCount ); } inline VkImageSubresourceLayers getSubresourceLayer( ImageSubresourceRange const & range ) { return getSubresourceLayers( range, 1u ); } } ================================================ FILE: include/RenderGraph/FrameGraphPrerequisites.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "FrameGraphFunctions.hpp" namespace crg { static constexpr PipelineColorBlendAttachmentState DefaultBlendState; template< typename TypeT > static inline const TypeT defaultV = DefaultValueGetterT< TypeT >::get(); template< typename TypeT > static inline TypeT getDefaultV() { return DefaultValueGetterT< TypeT >::get(); } template<> struct DefaultValueGetterT< VkPipelineVertexInputStateCreateInfo > { static VkPipelineVertexInputStateCreateInfo get() { VkPipelineVertexInputStateCreateInfo const result{ []() { return VkPipelineVertexInputStateCreateInfo{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO , nullptr , {} , {} , {} , {} , {} }; }() }; return result; } }; template< typename TypeT > using RawTypeT = typename RawTyperT< TypeT >::Type; } ================================================ FILE: include/RenderGraph/FrameGraphStructs.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "FrameGraphEnums.hpp" #include #include namespace crg { struct Offset2D { int32_t x{}; int32_t y{}; private: friend bool operator==( Offset2D const & lhs, Offset2D const & rhs )noexcept = default; }; struct Extent2D { uint32_t width{}; uint32_t height{}; private: friend bool operator==( Extent2D const & lhs, Extent2D const & rhs )noexcept = default; }; struct Rect2D { Offset2D offset{}; Extent2D extent{}; private: friend bool operator==( Rect2D const & lhs, Rect2D const & rhs )noexcept = default; }; struct Offset3D { int32_t x{}; int32_t y{}; int32_t z{}; private: friend bool operator==( Offset3D const & lhs, Offset3D const & rhs )noexcept = default; }; struct Extent3D { uint32_t width{}; uint32_t height{}; uint32_t depth{}; private: friend bool operator==( Extent3D const & lhs, Extent3D const & rhs )noexcept = default; }; struct Rect3D { Offset3D offset{}; Extent3D extent{}; private: friend bool operator==( Rect3D const & lhs, Rect3D const & rhs )noexcept = default; }; struct BufferSubresourceRange { DeviceSize offset{}; DeviceSize size{}; private: friend bool operator==( BufferSubresourceRange const & lhs, BufferSubresourceRange const & rhs )noexcept = default; friend std::strong_ordering operator<=>( BufferSubresourceRange const & lhs, BufferSubresourceRange const & rhs )noexcept { if ( auto c = lhs.offset <=> rhs.offset; c != std::strong_ordering::equal ) return c; return lhs.size <=> rhs.size; } }; struct ImageSubresourceRange { ImageAspectFlags aspectMask{}; uint32_t baseMipLevel{}; uint32_t levelCount{}; uint32_t baseArrayLayer{}; uint32_t layerCount{}; private: friend bool operator==( ImageSubresourceRange const & lhs, ImageSubresourceRange const & rhs )noexcept = default; friend std::strong_ordering operator<=>( ImageSubresourceRange const & lhs, ImageSubresourceRange const & rhs )noexcept { if ( auto c = lhs.aspectMask <=> rhs.aspectMask; c != std::strong_ordering::equal ) return c; if ( auto c = lhs.baseArrayLayer <=> rhs.baseArrayLayer; c != std::strong_ordering::equal ) return c; if ( auto c = lhs.layerCount <=> rhs.layerCount; c != std::strong_ordering::equal ) return c; if ( auto c = lhs.baseMipLevel <=> rhs.baseMipLevel; c != std::strong_ordering::equal ) return c; return lhs.levelCount <=> rhs.levelCount; } }; struct BufferCreateInfo { BufferCreateFlags flags{}; DeviceSize size{}; BufferUsageFlags usage{}; MemoryPropertyFlags memory{}; private: friend bool operator==( BufferCreateInfo const & lhs, BufferCreateInfo const & rhs )noexcept = default; }; struct BufferViewCreateInfo { PixelFormat format{}; BufferSubresourceRange subresourceRange{}; private: friend bool operator==( BufferViewCreateInfo const & lhs, BufferViewCreateInfo const & rhs )noexcept = default; }; struct ImageCreateInfo { ImageCreateFlags flags{}; ImageType imageType{}; PixelFormat format{}; Extent3D extent{}; uint32_t mipLevels{}; uint32_t arrayLayers{}; SampleCount samples{}; ImageTiling tiling{}; ImageUsageFlags usage{}; MemoryPropertyFlags memory{}; private: friend bool operator==( ImageCreateInfo const & lhs, ImageCreateInfo const & rhs )noexcept = default; }; struct ImageViewCreateInfo { ImageViewCreateFlags flags{}; ImageViewType viewType{}; PixelFormat format{}; ImageSubresourceRange subresourceRange{}; private: friend bool operator==( ImageViewCreateInfo const & lhs, ImageViewCreateInfo const & rhs )noexcept = default; }; struct ClearColorValue { enum class ValueIndex { eFloat32, eInt32, eUInt32, }; template< typename ValueT > constexpr ClearColorValue( ValueT r, ValueT g, ValueT b, ValueT a )noexcept : m_value{ std::array< ValueT, 4u >{ r, g, b, a } } { } constexpr explicit ClearColorValue( std::array< float, 4u > v = { 0.0f, 0.0f, 0.0f, 0.0f } )noexcept : m_value{ std::move( v ) } { } constexpr explicit ClearColorValue( std::array< int32_t, 4u > v )noexcept : m_value{ std::move( v ) } { } constexpr explicit ClearColorValue( std::array< uint32_t, 4u > v )noexcept : m_value{ std::move( v ) } { } constexpr bool isFloat32()const noexcept { return m_value.index() == uint32_t( ValueIndex::eFloat32 ); } constexpr bool isInt32()const noexcept { return m_value.index() == uint32_t( ValueIndex::eInt32 ); } constexpr bool isUInt32()const noexcept { return m_value.index() == uint32_t( ValueIndex::eUInt32 ); } constexpr std::array< float, 4u > const & float32()const noexcept { return std::get< uint32_t( ValueIndex::eFloat32 ) >( m_value ); } constexpr std::array< int32_t, 4u > const & int32()const noexcept { return std::get< uint32_t( ValueIndex::eInt32 ) >( m_value ); } constexpr std::array< uint32_t, 4u > const & uint32()const noexcept { return std::get< uint32_t( ValueIndex::eUInt32 ) >( m_value ); } private: std::variant< std::array< float, 4u > , std::array< int32_t, 4u > , std::array< uint32_t, 4u > > m_value; friend bool operator==( ClearColorValue const & lhs, ClearColorValue const & rhs )noexcept = default; }; struct ClearDepthStencilValue { float depth{}; uint32_t stencil{}; private: friend bool operator==( ClearDepthStencilValue const & lhs, ClearDepthStencilValue const & rhs )noexcept = default; }; struct ClearValue { enum class ValueIndex { eColor, eDepthStencil, }; constexpr explicit ClearValue( ClearColorValue v = ClearColorValue{} )noexcept : m_value{ std::move( v ) } { } constexpr explicit ClearValue( ClearDepthStencilValue v )noexcept : m_value{ std::move( v ) } { } constexpr bool isColor()const noexcept { return m_value.index() == uint32_t( ValueIndex::eColor ); } constexpr bool isDepthStencil()const noexcept { return m_value.index() == uint32_t( ValueIndex::eDepthStencil ); } constexpr ClearColorValue const & color()const noexcept { return std::get< uint32_t( ValueIndex::eColor ) >( m_value ); } constexpr ClearDepthStencilValue const & depthStencil()const noexcept { return std::get< uint32_t( ValueIndex::eDepthStencil ) >( m_value ); } private: std::variant< ClearColorValue, ClearDepthStencilValue > m_value; friend bool operator==( ClearValue const & lhs, ClearValue const & rhs )noexcept = default; }; struct PipelineColorBlendAttachmentState { uint32_t blendEnable{ VK_FALSE }; BlendFactor srcColorBlendFactor{ BlendFactor::eOne }; BlendFactor dstColorBlendFactor{ BlendFactor::eZero }; BlendOp colorBlendOp{ BlendOp::eAdd }; BlendFactor srcAlphaBlendFactor{ BlendFactor::eOne }; BlendFactor dstAlphaBlendFactor{ BlendFactor::eZero }; BlendOp alphaBlendOp{ BlendOp::eAdd }; ColorComponentFlags colorWriteMask{ ColorComponentFlags::eR | ColorComponentFlags::eG | ColorComponentFlags::eB | ColorComponentFlags::eA }; private: friend bool operator==( PipelineColorBlendAttachmentState const & lhs, PipelineColorBlendAttachmentState const & rhs )noexcept = default; }; struct PipelineState { AccessFlags access{}; PipelineStageFlags pipelineStage{}; private: friend bool operator==( PipelineState const & lhs, PipelineState const & rhs )noexcept = default; }; struct LayoutState { ImageLayout layout{}; PipelineState state{}; }; template< typename VkTypeT > struct ContextObjectT { ContextObjectT( ContextObjectT const & rhs ) = delete; ContextObjectT( ContextObjectT && rhs )noexcept = delete; ContextObjectT & operator=( ContextObjectT const & rhs ) = delete; ContextObjectT & operator=( ContextObjectT && rhs )noexcept = delete; explicit ContextObjectT( GraphContext & ctx , VkTypeT obj = {} , void( *dtor )( GraphContext &, VkTypeT & )noexcept = nullptr ) : context{ ctx } , object{ obj } , destroy{ dtor } { } ~ContextObjectT()noexcept { if ( destroy && object ) { destroy( context, object ); } } GraphContext & context; VkTypeT object; void ( *destroy )( GraphContext &, VkTypeT & )noexcept; }; struct SemaphoreWait { VkSemaphore semaphore{}; PipelineStageFlags dstStageMask{}; }; template< typename TypeT > struct RawTyperT { using Type = TypeT; }; } ================================================ FILE: include/RenderGraph/FramePass.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/Attachment.hpp" #include "RenderGraph/WriteDescriptorSet.hpp" #include #include #include namespace crg { using RunnablePassCreator = std::function< RunnablePassPtr( FramePass const & , GraphContext & , RunnableGraph & ) >; struct FramePass { public: struct SampledAttachment { SampledAttachment( Attachment const * attach, SamplerDesc sampler )noexcept : attach{ attach } , sampler{ std::move( sampler ) } { } Attachment const * attach; SamplerDesc sampler; }; public: /** *\name * Dependencies. */ /**@{*/ /** *\brief * Gets the attachment parent from the givent one. *\param[in] attach * The child attachment. */ CRG_API Attachment const * getParentAttachment( Attachment const & attach )const; /**@}*/ #pragma region Attachments /** *\name * Attachments */ /**@{*/ # pragma region Uniform /** *\name * Uniform */ /**@{*/ /** *\brief * Creates a uniform buffer multi-pass attachment. */ CRG_API void addInputUniformBuffer( BufferViewIdArray buffers , uint32_t binding ); /** *\brief * Creates a uniform buffer single-pass attachment. */ template< typename EnumT > void addInputUniformBufferT( BufferViewIdArray buffers , EnumT binding ) { addInputUniformBuffer( std::move( buffers ), uint32_t( binding ) ); } /** *\brief * Creates a uniform buffer single-pass attachment. */ void addInputUniformBuffer( BufferViewId buffer , uint32_t binding ) { addInputUniformBuffer( BufferViewIdArray{ buffer }, binding ); } /** *\brief * Creates a uniform buffer single-pass attachment. */ template< typename EnumT > void addInputUniformBufferT( BufferViewId buffer , EnumT binding ) { addInputUniformBufferT( BufferViewIdArray{ buffer }, binding ); } /** *\brief * Creates a sampled image multi-pass attachment. */ CRG_API void addInputSampledImage( ImageViewIdArray views , uint32_t binding , SamplerDesc samplerDesc = SamplerDesc{} ); /** *\brief * Creates a sampled image single-pass attachment. */ template< typename EnumT > void addInputSampledImageT( ImageViewIdArray views , EnumT binding , SamplerDesc samplerDesc = SamplerDesc{} ) { addInputSampledImage( std::move( views ), uint32_t( binding ), std::move( samplerDesc ) ); } /** *\brief * Creates a sampled image single-pass attachment. */ void addInputSampledImage( ImageViewId view , uint32_t binding , SamplerDesc samplerDesc = SamplerDesc{} ) { addInputSampledImage( ImageViewIdArray{ view }, binding, std::move( samplerDesc ) ); } /** *\brief * Creates a sampled image single-pass attachment. */ template< typename EnumT > void addInputSampledImageT( ImageViewId view , EnumT binding , SamplerDesc samplerDesc = SamplerDesc{} ) { addInputSampledImageT( ImageViewIdArray{ view }, binding, std::move( samplerDesc ) ); } /** *\brief * Creates an input uniform attachment. */ CRG_API void addInputUniform( Attachment const & attach , uint32_t binding ); /** *\brief * Creates an input uniform attachment. */ template< typename EnumT > void addInputUniformT( Attachment const & attach , EnumT binding ) { addInputUniform( attach, uint32_t( binding ) ); } /** *\brief * Creates a sampled image attachment. */ CRG_API void addInputSampled( Attachment const & attach , uint32_t binding , SamplerDesc samplerDesc = SamplerDesc{} ); /** *\brief * Creates a sampled image attachment. */ template< typename EnumT > void addInputSampledT( Attachment const & attach , EnumT binding , SamplerDesc samplerDesc = SamplerDesc{} ) { addInputSampled( attach, uint32_t( binding ), std::move( samplerDesc ) ); } /**@}*/ # pragma endregion # pragma region Storage /** *\name * Storage */ /**@{*/ /** *\brief * Creates a storage buffer multi-pass attachment. */ CRG_API void addInputStorageBuffer( BufferViewIdArray buffers , uint32_t binding ); /** *\brief * Creates a storage buffer multi-pass attachment. */ template< typename EnumT > void addInputStorageBufferT( BufferViewIdArray buffers , EnumT binding ) { addInputStorageBuffer( std::move( buffers ), uint32_t( binding ) ); } /** *\brief * Creates a storage buffer single-pass attachment. */ void addInputStorageBuffer( BufferViewId buffer , uint32_t binding ) { addInputStorageBuffer( BufferViewIdArray{ buffer }, binding ); } /** *\brief * Creates a storage buffer single-pass attachment. */ template< typename EnumT > void addInputStorageBufferT( BufferViewId buffer , EnumT binding ) { addInputStorageBufferT( BufferViewIdArray{ buffer }, binding ); } /** *\brief * Creates an input storage attachment. */ CRG_API void addInputStorageImage( ImageViewIdArray views , uint32_t binding ); /** *\brief * Creates an input storage attachment. */ template< typename EnumT > void addInputStorageImageT( ImageViewIdArray views , EnumT binding ) { addInputStorageImage( std::move( views ), uint32_t( binding ) ); } /** *\brief * Creates an input storage attachment. */ void addInputStorageImage( ImageViewId view , uint32_t binding ) { addInputStorageImage( ImageViewIdArray{ view }, binding ); } /** *\brief * Creates an input storage attachment. */ template< typename EnumT > void addInputStorageImageT( ImageViewId view , EnumT binding ) { addInputStorageImageT( ImageViewIdArray{ view }, binding ); } /** *\brief * Creates an input storage attachment. */ CRG_API void addInputStorage( Attachment const & attach , uint32_t binding ); /** *\brief * Creates an input storage attachment. */ template< typename EnumT > void addInputStorageT( Attachment const & attach , EnumT binding ) { addInputStorage( attach, uint32_t( binding ) ); } /** *\brief * Creates an input/output storage attachment. */ CRG_API Attachment const * addInOutStorage( Attachment const & attach , uint32_t binding ); /** *\brief * Creates an input/output storage attachment. */ template< typename EnumT > Attachment const * addInOutStorageT( Attachment const & attach , EnumT binding ) { return addInOutStorage( attach, uint32_t( binding ) ); } /** *\brief * Creates an output storage buffer multi-pass attachment. */ CRG_API Attachment const * addOutputStorageBuffer( BufferViewIdArray buffers , uint32_t binding ); /** *\brief * Creates an output storage buffer multi-pass attachment. */ template< typename EnumT > Attachment const * addOutputStorageBufferT( BufferViewIdArray buffers , EnumT binding ) { return addOutputStorageBuffer( std::move( buffers ), uint32_t( binding ) ); } /** *\brief * Creates an output storage buffer single-pass attachment. */ Attachment const * addOutputStorageBuffer( BufferViewId buffer , uint32_t binding ) { return addOutputStorageBuffer( BufferViewIdArray{ buffer }, binding ); } /** *\brief * Creates an output storage buffer single-pass attachment. */ template< typename EnumT > Attachment const * addOutputStorageBufferT( BufferViewId buffer , EnumT binding ) { return addOutputStorageBufferT( BufferViewIdArray{ buffer }, binding ); } /** *\brief * Creates a storage buffer multi-pass attachment that will be cleared a the beginning of the pass. */ CRG_API Attachment const * addClearableOutputStorageBuffer( BufferViewIdArray buffers , uint32_t binding ); /** *\brief * Creates a storage buffer multi-pass attachment that will be cleared a the beginning of the pass. */ template< typename EnumT > Attachment const * addClearableOutputStorageBufferT( BufferViewIdArray buffers , EnumT binding ) { return addClearableOutputStorageBuffer( std::move( buffers ), uint32_t( binding ) ); } /** *\brief * Creates a storage buffer single-pass attachment that will be cleared a the beginning of the pass. */ Attachment const * addClearableOutputStorageBuffer( BufferViewId buffer , uint32_t binding ) { return addClearableOutputStorageBuffer( BufferViewIdArray{ buffer }, binding ); } /** *\brief * Creates a storage buffer single-pass attachment that will be cleared a the beginning of the pass. */ template< typename EnumT > Attachment const * addClearableOutputStorageBufferT( BufferViewId buffer , EnumT binding ) { return addClearableOutputStorageBufferT( BufferViewIdArray{ buffer }, binding ); } /** *\brief * Creates a storage image multi-pass attachment. */ CRG_API Attachment const * addOutputStorageImage( ImageViewIdArray view , uint32_t binding ); /** *\brief * Creates a storage image multi-pass attachment. */ template< typename EnumT > Attachment const * addOutputStorageImageT( ImageViewIdArray views , EnumT binding ) { return addOutputStorageImage( std::move( views ), uint32_t( binding ) ); } /** *\brief * Creates a storage image single-pass attachment. */ Attachment const * addOutputStorageImage( ImageViewId view , uint32_t binding ) { return addOutputStorageImage( ImageViewIdArray{ view }, binding ); } /** *\brief * Creates a storage image single-pass attachment. */ template< typename EnumT > Attachment const * addOutputStorageImageT( ImageViewId view , EnumT binding ) { return addOutputStorageImageT( ImageViewIdArray{ view }, binding ); } /** *\brief * Creates a storage image multi-pass attachment. */ CRG_API Attachment const * addClearableOutputStorageImage( ImageViewIdArray views , uint32_t binding , ClearValue clearValue = ClearValue{} ); /** *\brief * Creates a storage image multi-pass attachment. */ template< typename EnumT > Attachment const * addClearableOutputStorageImageT( ImageViewIdArray views , EnumT binding , ClearValue clearValue = ClearValue{} ) { return addClearableOutputStorageImage( std::move( views ) , uint32_t( binding ) , std::move( clearValue ) ); } /** *\brief * Creates a storage image single-pass attachment. */ Attachment const * addClearableOutputStorageImage( ImageViewId view , uint32_t binding , ClearValue clearValue = ClearValue{} ) { return addClearableOutputStorageImage( ImageViewIdArray{ view } , binding , std::move( clearValue ) ); } /** *\brief * Creates a storage image single-pass attachment. */ template< typename EnumT > Attachment const * addClearableOutputStorageImageT( ImageViewId view , EnumT binding , ClearValue clearValue = ClearValue{} ) { return addClearableOutputStorageImageT( ImageViewIdArray{ view } , binding , std::move( clearValue ) ); } /**@}*/ # pragma endregion # pragma region Transfer /** *\name * Transfer */ /**@{*/ /** *\brief * Creates a transfer input external buffer. */ CRG_API void addInputTransferBuffer( BufferViewIdArray views ); /** *\brief * Creates a transfer input external buffer. */ void addInputTransferBuffer( BufferViewId view ) { addInputTransferBuffer( BufferViewIdArray{ view } ); } /** *\brief * Creates a transfer input external image. */ CRG_API void addInputTransferImage( ImageViewIdArray views ); /** *\brief * Creates a transfer input external image. */ void addInputTransferImage( ImageViewId view ) { addInputTransferImage( ImageViewIdArray{ view } ); } /** *\brief * Creates a transfer input attachment. */ CRG_API void addInputTransfer( Attachment const & attach ); /** *\brief * Creates a transfer input/output attachment. */ CRG_API Attachment const * addInOutTransfer( Attachment const & attach , Attachment::Flag flag = {} ); /** *\brief * Creates a transfer output buffer multi-pass attachment. */ CRG_API Attachment const * addOutputTransferBuffer( BufferViewIdArray buffers ); /** *\brief * Creates a transfer output buffer single-pass attachment. */ Attachment const * addOutputTransferBuffer( BufferViewId buffer ) { return addOutputTransferBuffer( BufferViewIdArray{ buffer } ); } /** *\brief * Creates a transfer output multi-pass attachment. */ CRG_API Attachment const * addOutputTransferImage( ImageViewIdArray view ); /** *\brief * Creates an transfer output single-pass attachment. */ Attachment const * addOutputTransferImage( ImageViewId view ) { return addOutputTransferImage( ImageViewIdArray{ view } ); } /**@}*/ # pragma endregion # pragma region Target /** *\name * Target */ /**@{*/ /** *\brief * Creates an input colour attachment. */ CRG_API void addInputColourTargetImage( ImageViewIdArray views ); /** *\brief * Creates an input colour attachment. */ void addInputColourTargetImage( ImageViewId view ) { return addInputColourTargetImage( ImageViewIdArray{ view } ); } /** *\brief * Creates an input depth attachment. */ CRG_API void addInputDepthTargetImage( ImageViewIdArray views ); /** *\brief * Creates an input depth attachment. */ void addInputDepthTargetImage( ImageViewId view ) { return addInputDepthTargetImage( ImageViewIdArray{ view } ); } /** *\brief * Creates an input stencil attachment. */ CRG_API void addInputStencilTargetImage( ImageViewIdArray views ); /** *\brief * Creates an input stencil attachment. */ void addInputStencilTargetImage( ImageViewId view ) { return addInputStencilTargetImage( ImageViewIdArray{ view } ); } /** *\brief * Creates an input depth and stencil attachment. */ CRG_API void addInputDepthStencilTargetImage( ImageViewIdArray views ); /** *\brief * Creates an input depth and stencil attachment. */ void addInputDepthStencilTargetImage( ImageViewId view ) { return addInputDepthStencilTargetImage( ImageViewIdArray{ view } ); } /** *\brief * Creates an input colour attachment. */ CRG_API void addInputColourTarget( Attachment const & attach ); /** *\brief * Creates an input depth attachment. */ CRG_API void addInputDepthTarget( Attachment const & attach ); /** *\brief * Creates an input stencil attachment. */ CRG_API void addInputStencilTarget( Attachment const & attach ); /** *\brief * Creates an input depth and stencil attachment. */ CRG_API void addInputDepthStencilTarget( Attachment const & attach ); /** *\brief * Creates an in/out colour attachment. */ CRG_API Attachment const * addInOutColourTarget( Attachment const & attach , PipelineColorBlendAttachmentState blendState = DefaultBlendState ); /** *\brief * Creates an in/out depth attachment. */ CRG_API Attachment const * addInOutDepthTarget( Attachment const & attach ); /** *\brief * Creates an in/out stencil attachment. */ CRG_API Attachment const * addInOutStencilTarget( Attachment const & attach ); /** *\brief * Creates an in/out depth and stencil attachment. */ CRG_API Attachment const * addInOutDepthStencilTarget( Attachment const & attach ); /** *\brief * Creates an output colour multi-pass attachment. */ CRG_API Attachment const * addOutputColourTarget( ImageViewIdArray views , ClearColorValue clearValue = ClearColorValue{} ); /** *\brief * Creates an output colour single-pass attachment. */ Attachment const * addOutputColourTarget( ImageViewId view , ClearColorValue clearValue = ClearColorValue{} ) { return addOutputColourTarget( ImageViewIdArray{ view } , std::move( clearValue ) ); } /** *\brief * Creates an output depth multi-pass attachment. */ CRG_API Attachment const * addOutputDepthTarget( ImageViewIdArray views , ClearDepthStencilValue clearValue = ClearDepthStencilValue{} ); /** *\brief * Creates an output depth single-pass attachment. */ Attachment const * addOutputDepthTarget( ImageViewId view , ClearDepthStencilValue clearValue = ClearDepthStencilValue{} ) { return addOutputDepthTarget( ImageViewIdArray{ view } , std::move( clearValue ) ); } /** *\brief * Creates an output stencil multi-pass attachment. */ CRG_API Attachment const * addOutputStencilTarget( ImageViewIdArray views , ClearDepthStencilValue clearValue = ClearDepthStencilValue{} ); /** *\brief * Creates an output stencil single-pass attachment. */ Attachment const * addOutputStencilTarget( ImageViewId view , ClearDepthStencilValue clearValue = ClearDepthStencilValue{} ) { return addOutputStencilTarget( ImageViewIdArray{ view } , std::move( clearValue ) ); } /** *\brief * Creates an output depth and stencil multi-pass attachment. */ CRG_API Attachment const * addOutputDepthStencilTarget( ImageViewIdArray views , ClearDepthStencilValue clearValue = ClearDepthStencilValue{} ); /** *\brief * Creates an output depth and stencil single-pass attachment. */ Attachment const * addOutputDepthStencilTarget( ImageViewId view , ClearDepthStencilValue clearValue = ClearDepthStencilValue{} ) { return addOutputDepthStencilTarget( ImageViewIdArray{ view } , std::move( clearValue ) ); } /**@}*/ /**@}*/ # pragma endregion # pragma region Implicit /** *\name * Implicit */ /**@{*/ /** *\brief * Creates an implicit attachment. *\remarks * This attachment will only be used to compute dependencies, and is considered an input, in that goal. */ CRG_API void addImplicit( Attachment const & attach , AccessState wantedAccess ); /** *\brief * Creates an implicit attachment. *\remarks * This attachment will only be used to compute dependencies, and is considered an input, in that goal. */ CRG_API void addImplicit( Attachment const & attach , ImageLayout wantedLayout ); /**@}*/ # pragma endregion /**@}*/ #pragma endregion /** *\name * Graph compilation. */ /**@{*/ CRG_API RunnablePassPtr createRunnable( GraphContext & context , RunnableGraph & graph )const; /**@}*/ CRG_API std::string getFullName()const; CRG_API std::string getGroupName()const; std::string const & getName()const { return m_name; } auto begin()const { return m_ownAttaches.cbegin(); } auto end()const { return m_ownAttaches.cend(); } FramePassGroup const & getGroup()const noexcept { return m_group; } FrameGraph const & getGraph()const noexcept { return m_graph; } uint32_t getId()const noexcept { return m_id; } std::map< uint32_t, Attachment const * > const & getUniforms()const noexcept { return m_uniforms; } std::map< uint32_t, SampledAttachment > const & getSampled()const noexcept { return m_sampled; } std::map< uint32_t, Attachment const * > const & getInputs()const noexcept { return m_inputs; } std::map< uint32_t, Attachment const * > const & getInouts()const noexcept { return m_inouts; } std::map< uint32_t, Attachment const * > const & getOutputs()const noexcept { return m_outputs; } std::vector< Attachment const * > const & getTargets()const noexcept { return m_targets; } protected: friend struct FramePassGroup; /** *\name * Construction. */ /**@{*/ CRG_API FramePass( FramePassGroup const & group , FrameGraph & graph , uint32_t id , std::string const & name , RunnablePassCreator runnableCreator ); /**@}*/ private: CRG_API Attachment const * addColourTarget( std::string const & name , Attachment::FlagKind flags , ImageViewIdArray views , AttachmentLoadOp loadOp , AttachmentStoreOp storeOp , ImageLayout wantedLayout = ImageLayout::eUndefined , ClearColorValue clearValue = ClearColorValue{} , PipelineColorBlendAttachmentState blendState = DefaultBlendState ); CRG_API Attachment const * addDepthTarget( std::string const & name , Attachment::FlagKind flags , ImageViewIdArray views , AttachmentLoadOp loadOp , AttachmentStoreOp storeOp , ImageLayout wantedLayout = ImageLayout::eUndefined , ClearDepthStencilValue clearValue = ClearDepthStencilValue{} ); CRG_API Attachment const * addStencilTarget( std::string const & name , Attachment::FlagKind flags , ImageAttachment::FlagKind stencilFlags , ImageViewIdArray views , AttachmentLoadOp stencilLoadOp , AttachmentStoreOp stencilStoreOp , ImageLayout wantedLayout = ImageLayout::eUndefined , ClearDepthStencilValue clearValue = ClearDepthStencilValue{} ); CRG_API Attachment const * addDepthStencilTarget( std::string const & name , Attachment::FlagKind flags , ImageAttachment::FlagKind stencilFlags , ImageViewIdArray views , AttachmentLoadOp loadOp , AttachmentStoreOp storeOp , AttachmentLoadOp stencilLoadOp , AttachmentStoreOp stencilStoreOp , ImageLayout wantedLayout = ImageLayout::eUndefined , ClearDepthStencilValue clearValue = ClearDepthStencilValue{} ); Attachment const * addOwnAttach( ImageViewIdArray views , std::string attachName , Attachment::FlagKind flags , ImageAttachment::FlagKind imageFlags , AttachmentLoadOp loadOp , AttachmentStoreOp storeOp , AttachmentLoadOp stencilLoadOp , AttachmentStoreOp stencilStoreOp , ClearValue clearValue , PipelineColorBlendAttachmentState blendState , ImageLayout wantedLayout , Attachment const * parent ); Attachment const * addOwnAttach( BufferViewIdArray views , std::string attachName , Attachment::FlagKind flags , BufferAttachment::FlagKind bufferFlags , AccessState access , Attachment const * parent ); Attachment * addOwnAttach( Attachment * mine , Attachment const * parent ); private: FramePassGroup const & m_group; FrameGraph & m_graph; uint32_t m_id; std::map< uint32_t, Attachment const * > m_uniforms; std::map< uint32_t, SampledAttachment > m_sampled; std::map< uint32_t, Attachment const * > m_inputs; std::map< uint32_t, Attachment const * > m_inouts; std::map< uint32_t, Attachment const * > m_outputs; std::vector< Attachment const * > m_targets; RunnablePassCreator m_runnableCreator; std::string m_name; struct OwnAttachment { AttachmentPtr mine{}; Attachment const * parent{}; }; std::unordered_map< Attachment const *, OwnAttachment > m_ownAttaches; }; } ================================================ FILE: include/RenderGraph/FramePassGroup.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/FramePass.hpp" #include namespace crg { struct FramePassGroup { friend class FrameGraph; class Token { friend class FrameGraph; friend struct FramePassGroup; private: Token() noexcept = default; }; /** *\name * Construction. */ /**@{*/ CRG_API FramePassGroup( FrameGraph & graph , uint32_t id , std::string const & name , Token token ); CRG_API FramePassGroup( FramePassGroup & parent , uint32_t id , std::string const & name , Token token ); /**@}*/ public: /** *\name * Passes. */ /**@{*/ CRG_API FramePass & createPass( std::string const & name , RunnablePassCreator runnableCreator ); CRG_API FramePassGroup & createPassGroup( std::string const & name ); CRG_API bool hasPass( std::string const & name )const; CRG_API void listPasses( FramePassArray & result )const; /**@}*/ /** *\name * Group I/O. */ /**@{*/ CRG_API void addGroupInput( ImageViewId view ); CRG_API void addGroupOutput( ImageViewId view ); /**@}*/ /** *\name * Graph interface. */ /**@{*/ /** *\copydoc crg::FrameGraph::getFinalLayoutState */ CRG_API LayoutState getFinalLayoutState( ImageViewId view , uint32_t passIndex = 0u )const; /** *\copydoc crg::FrameGraph::createBuffer */ CRG_API BufferId createBuffer( BufferData const & img )const; /** *\copydoc crg::FrameGraph::createView */ CRG_API BufferViewId createView( BufferViewData const & view )const; /** *\copydoc crg::FrameGraph::createImage */ CRG_API ImageId createImage( ImageData const & img )const; /** *\copydoc crg::FrameGraph::createView */ CRG_API ImageViewId createView( ImageViewData const & view )const; /** *\copydoc crg::FrameGraph::addInput */ CRG_API void addInput( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range , LayoutState const & outputLayout ); /** *\copydoc crg::FrameGraph::addInput */ CRG_API void addInput( ImageViewId view , LayoutState const & outputLayout ); /** *\copydoc crg::FrameGraph::addOutput */ CRG_API void addOutput( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range , LayoutState const & outputLayout ); /** *\copydoc crg::FrameGraph::addOutput */ CRG_API void addOutput( ImageViewId view , LayoutState const & outputLayout ); /** *\copydoc crg::FrameGraph::mergeViews */ CRG_API ImageViewId mergeViews( ImageViewIdArray const & views , bool mergeMipLevels = true , bool mergeArrayLayers = true ); /** *\copydoc crg::FrameGraph::mergeViews */ CRG_API BufferViewId mergeViews( BufferViewIdArray const & views ); /** *\copydoc crg::FrameGraph::mergeAttachments */ CRG_API Attachment const * mergeAttachments( AttachmentArray const & attachments , bool mergeMipLevels = true , bool mergeArrayLayers = true ); /**@}*/ CRG_API std::string getFullName()const; std::string const & getName()const noexcept { return m_name; } FramePassGroupPtrArray const & getGroups()const noexcept { return m_groups; } FramePassPtrArray const & getPasses()const noexcept { return m_passes; } FramePassGroup const * getParent()const noexcept { return m_parent; } uint32_t getId()const noexcept { return m_id; } private: uint32_t m_id; FramePassPtrArray m_passes; FramePassGroupPtrArray m_groups; FramePassGroup * m_parent{}; std::string m_name; FrameGraph & m_graph; std::unordered_set< uint32_t > m_inputs; std::unordered_set< uint32_t > m_outputs; }; } ================================================ FILE: include/RenderGraph/FramePassTimer.hpp ================================================ /* See LICENSE file in root folder */ #ifndef ___CRG_RenderPassTimer_H___ #define ___CRG_RenderPassTimer_H___ #include "FrameGraphPrerequisites.hpp" #include "Signal.hpp" #include #include namespace crg { using Clock = std::chrono::high_resolution_clock; using Nanoseconds = std::chrono::nanoseconds; using FramePassDestroyFunc = std::function< void( FramePassTimer & ) >; using OnFramePassDestroy = Signal< FramePassDestroyFunc >; using OnFramePassDestroyConnection = SignalConnection< OnFramePassDestroy >; using PassColour = std::array< float, 4u >; enum class TimerScope { eGraph, ePass, eUpdate, }; class FramePassTimerBlock { public: CRG_API explicit FramePassTimerBlock( FramePassTimer & timer ); CRG_API FramePassTimerBlock( FramePassTimerBlock && rhs )noexcept; FramePassTimerBlock & operator=( FramePassTimerBlock && rhs )noexcept = delete; FramePassTimerBlock( FramePassTimerBlock const & ) = delete; FramePassTimerBlock & operator=( FramePassTimerBlock const & ) = delete; CRG_API ~FramePassTimerBlock()noexcept; FramePassTimer * operator->()const { return m_timer; } private: FramePassTimer * m_timer; }; class FramePassTimer { friend class FramePassTimerBlock; public: FramePassTimer( FramePassTimer const & rhs ) = delete; FramePassTimer( FramePassTimer && rhs )noexcept = delete; FramePassTimer & operator=( FramePassTimer const & rhs ) = delete; FramePassTimer & operator=( FramePassTimer && rhs )noexcept = delete; /** *\brief * Reserves queries from given pool. *\param[in] device * The GPU device. *\param[in] category * The render pass category. *\param[in] name * The timer name. */ CRG_API FramePassTimer( GraphContext & context , std::string const & name , TimerScope scope , VkQueryPool timerQueries , uint32_t & baseQueryOffset ); /** *\brief * Owns its query pool. *\param[in] device * The GPU device. *\param[in] category * The render pass category. *\param[in] name * The timer name. */ CRG_API FramePassTimer( GraphContext & context , std::string const & name , TimerScope scope ); CRG_API ~FramePassTimer()noexcept; /** *\brief * Starts the CPU timer, resets GPU time. */ CRG_API FramePassTimerBlock start(); /** *\brief * Notifies the given pass render. *\param[in] passIndex * The pass index. */ CRG_API void notifyPassRender( uint32_t passIndex = 0u )noexcept; /** *\brief * Reset the timer's times. */ CRG_API void reset()noexcept; /** *\brief * Writes the timestamp for the beginning of the pass. *\param[in] cmd * The command buffer used to record the begin timestamp. *\param[in] passId * The pass ID. */ CRG_API void beginPass( VkCommandBuffer commandBuffer , std::string const & groupName , uint32_t passId )noexcept; /** *\brief * Writes the timestamp for the end of the pass. *\param[in] cmd * The command buffer used to record the end timestamp. *\param[in] passIndex * The pass index. */ CRG_API void endPass( VkCommandBuffer commandBuffer )noexcept; /** *\brief * Retrieves GPU time from the query. */ CRG_API void retrieveGpuTime()noexcept; /** *\name * Getters. */ /**@{*/ Nanoseconds getCpuTime()const noexcept { return m_cpuTime; } Nanoseconds getGpuTime()const noexcept { return m_gpuTime; } std::string const & getName()const noexcept { return m_name; } PassColour const & getColour()const noexcept { return m_colour; } TimerScope getScope()const noexcept { return m_scope; } /**@}*/ OnFramePassDestroy onDestroy; private: void stop()noexcept; private: GraphContext & m_context; TimerScope m_scope{}; std::string m_name{}; PassColour m_colour; Clock::time_point m_cpuSaveTime{}; Nanoseconds m_cpuTime{}; Nanoseconds m_gpuTime{}; VkQueryPool m_timerQueries{}; bool m_ownPool{}; struct Query { uint32_t offset{}; bool written{}; bool started{}; }; std::array< Query, 2u > m_queries; }; } #endif ================================================ FILE: include/RenderGraph/GraphContext.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "FrameGraphPrerequisites.hpp" #include #include #include #include #pragma warning( push ) #pragma warning( disable: 4365 ) #pragma warning( disable: 5262 ) #include #pragma warning( pop ) namespace crg { template< typename ObjectT > struct DebugTypeTraits; template<> struct DebugTypeTraits< VkBuffer > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_BUFFER; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT; #endif static inline std::string Name{ "VkBuffer" }; }; template<> struct DebugTypeTraits< VkCommandBuffer > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_COMMAND_BUFFER; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT; #endif static inline std::string Name{ "VkCommandBuffer" }; }; template<> struct DebugTypeTraits< VkDevice > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_PHYSICAL_DEVICE; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT; #endif static inline std::string Name{ "VkDevice" }; }; template<> struct DebugTypeTraits< VkInstance > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_INSTANCE; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT; #endif static inline std::string Name{ "VkInstance" }; }; template<> struct DebugTypeTraits< VkPhysicalDevice > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_PHYSICAL_DEVICE; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT; #endif static inline std::string Name{ "VkPhysicalDevice" }; }; template<> struct DebugTypeTraits< VkQueue > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_QUEUE; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT; #endif static inline std::string Name{ "VkQueue" }; }; #if ( VK_USE_64_BIT_PTR_DEFINES == 1 ) template<> struct DebugTypeTraits< VkBufferView > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_BUFFER_VIEW; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT; #endif static inline std::string Name{ "VkBufferView" }; }; template<> struct DebugTypeTraits< VkCommandPool > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_COMMAND_POOL; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT; #endif static inline std::string Name{ "VkCommandPool" }; }; template<> struct DebugTypeTraits< VkDescriptorPool > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_DESCRIPTOR_POOL; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT; #endif static inline std::string Name{ "VkDescriptorPool" }; }; template<> struct DebugTypeTraits< VkDescriptorSet > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_DESCRIPTOR_SET; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT; #endif static inline std::string Name{ "VkDescriptorSet" }; }; template<> struct DebugTypeTraits< VkDescriptorSetLayout > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT; #endif static inline std::string Name{ "VkDescriptorSetLayout" }; }; template<> struct DebugTypeTraits< VkDeviceMemory > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_DEVICE_MEMORY; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT; #endif static inline std::string Name{ "VkDeviceMemory" }; }; template<> struct DebugTypeTraits< VkEvent > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_EVENT; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT; #endif static inline std::string Name{ "VkEvent" }; }; template<> struct DebugTypeTraits< VkFence > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_FENCE; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT; #endif static inline std::string Name{ "VkFence" }; }; template<> struct DebugTypeTraits< VkFramebuffer > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_FRAMEBUFFER; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT; #endif static inline std::string Name{ "VkFramebuffer" }; }; template<> struct DebugTypeTraits< VkImage > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_IMAGE; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT; #endif static inline std::string Name{ "VkImage" }; }; template<> struct DebugTypeTraits< VkImageView > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_IMAGE_VIEW; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT; #endif static inline std::string Name{ "VkImageView" }; }; template<> struct DebugTypeTraits< VkPipeline > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_PIPELINE; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT; #endif static inline std::string Name{ "VkPipeline" }; }; template<> struct DebugTypeTraits< VkPipelineLayout > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_PIPELINE_LAYOUT; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT; #endif static inline std::string Name{ "VkPipelineLayout" }; }; template<> struct DebugTypeTraits< VkQueryPool > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_QUERY_POOL; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT; #endif static inline std::string Name{ "VkQueryPool" }; }; template<> struct DebugTypeTraits< VkRenderPass > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_RENDER_PASS; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT; #endif static inline std::string Name{ "VkRenderPass" }; }; template<> struct DebugTypeTraits< VkSampler > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_SAMPLER; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT; #endif static inline std::string Name{ "VkSampler" }; }; template<> struct DebugTypeTraits< VkSemaphore > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_SEMAPHORE; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT; #endif static inline std::string Name{ "VkSemaphore" }; }; template<> struct DebugTypeTraits< VkShaderModule > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_SHADER_MODULE; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT; #endif static inline std::string Name{ "VkShaderModule" }; }; template<> struct DebugTypeTraits< VkSurfaceKHR > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_SURFACE_KHR; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT; #endif static inline std::string Name{ "VkSurfaceKHR" }; }; template<> struct DebugTypeTraits< VkSwapchainKHR > { #if VK_EXT_debug_utils static VkObjectType constexpr UtilsValue = VK_OBJECT_TYPE_SWAPCHAIN_KHR; #endif #if VK_EXT_debug_report || VK_EXT_debug_marker static VkDebugReportObjectTypeEXT constexpr ReportValue = VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT; #endif static inline std::string Name{ "VkSwapchainKHR" }; }; #endif // ( VK_USE_64_BIT_PTR_DEFINES == 1 ) struct DebugBlockInfo { std::string markerName; std::array< float, 4 > colour; }; struct DeletionQueue { using CDtorFunc = void( GraphContext & ); using DtorFunc = std::function< CDtorFunc >; using DtorFuncArray = std::vector< DtorFunc >; public: void push( DtorFunc func ) { m_toDelete.push_back( std::move( func ) ); } void clear( GraphContext & context ) { DtorFuncArray tmp{ std::move( m_toDelete ) }; for ( DtorFunc const & func : tmp ) { func( context ); } } private: DtorFuncArray m_toDelete; }; struct GraphContext { GraphContext( GraphContext const & ) = delete; GraphContext( GraphContext && ) = delete; GraphContext & operator=( GraphContext const & ) = delete; GraphContext & operator=( GraphContext && ) = delete; CRG_API GraphContext( VkDevice device , VkPipelineCache cache , VkAllocationCallbacks const * allocator , VkPhysicalDeviceMemoryProperties memoryProperties , VkPhysicalDeviceProperties properties , bool separateDepthStencilLayouts , PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr ); CRG_API ~GraphContext()noexcept; VkDevice device{}; VkPipelineCache cache{}; VkAllocationCallbacks const * allocator{}; VkPhysicalDeviceMemoryProperties memoryProperties{}; VkPhysicalDeviceProperties properties{}; VkPhysicalDeviceFeatures features{}; bool separateDepthStencilLayouts; DeletionQueue delQueue; #define DECL_vkFunction( name )\ PFN_vk##name vk##name{ nullptr } DECL_vkFunction( CreateGraphicsPipelines ); DECL_vkFunction( CreateComputePipelines ); DECL_vkFunction( DestroyPipeline ); DECL_vkFunction( CreatePipelineLayout ); DECL_vkFunction( DestroyPipelineLayout ); DECL_vkFunction( CreateDescriptorSetLayout ); DECL_vkFunction( DestroyDescriptorSetLayout ); DECL_vkFunction( CreateDescriptorPool ); DECL_vkFunction( DestroyDescriptorPool ); DECL_vkFunction( AllocateDescriptorSets ); DECL_vkFunction( FreeDescriptorSets ); DECL_vkFunction( CreateBuffer ); DECL_vkFunction( DestroyBuffer ); DECL_vkFunction( CreateBufferView ); DECL_vkFunction( DestroyBufferView ); DECL_vkFunction( GetBufferMemoryRequirements ); DECL_vkFunction( GetImageMemoryRequirements ); DECL_vkFunction( AllocateMemory ); DECL_vkFunction( FreeMemory ); DECL_vkFunction( BindBufferMemory ); DECL_vkFunction( BindImageMemory ); DECL_vkFunction( MapMemory ); DECL_vkFunction( UnmapMemory ); DECL_vkFunction( FlushMappedMemoryRanges ); DECL_vkFunction( InvalidateMappedMemoryRanges ); DECL_vkFunction( CreateRenderPass ); DECL_vkFunction( DestroyRenderPass ); DECL_vkFunction( CreateFramebuffer ); DECL_vkFunction( DestroyFramebuffer ); DECL_vkFunction( CreateImage ); DECL_vkFunction( DestroyImage ); DECL_vkFunction( CreateImageView ); DECL_vkFunction( DestroyImageView ); DECL_vkFunction( CreateSampler ); DECL_vkFunction( DestroySampler ); DECL_vkFunction( CreateCommandPool ); DECL_vkFunction( DestroyCommandPool ); DECL_vkFunction( AllocateCommandBuffers ); DECL_vkFunction( FreeCommandBuffers ); DECL_vkFunction( CreateSemaphore ); DECL_vkFunction( DestroySemaphore ); DECL_vkFunction( UpdateDescriptorSets ); DECL_vkFunction( BeginCommandBuffer ); DECL_vkFunction( EndCommandBuffer ); DECL_vkFunction( QueueSubmit ); DECL_vkFunction( CreateQueryPool ); DECL_vkFunction( DestroyQueryPool ); DECL_vkFunction( GetQueryPoolResults ); DECL_vkFunction( ResetCommandBuffer ); DECL_vkFunction( CreateEvent ); DECL_vkFunction( DestroyEvent ); DECL_vkFunction( ResetEvent ); DECL_vkFunction( SetEvent ); DECL_vkFunction( GetEventStatus ); DECL_vkFunction( CreateFence ); DECL_vkFunction( DestroyFence ); DECL_vkFunction( GetFenceStatus ); DECL_vkFunction( WaitForFences ); DECL_vkFunction( ResetFences ); DECL_vkFunction( CmdBindPipeline ); DECL_vkFunction( CmdBindDescriptorSets ); DECL_vkFunction( CmdBindVertexBuffers ); DECL_vkFunction( CmdBindIndexBuffer ); DECL_vkFunction( CmdClearColorImage ); DECL_vkFunction( CmdClearDepthStencilImage ); DECL_vkFunction( CmdDispatch ); DECL_vkFunction( CmdDispatchIndirect ); DECL_vkFunction( CmdDraw ); DECL_vkFunction( CmdDrawIndexed ); DECL_vkFunction( CmdDrawIndexedIndirect ); DECL_vkFunction( CmdDrawIndirect ); DECL_vkFunction( CmdBeginRenderPass ); DECL_vkFunction( CmdEndRenderPass ); DECL_vkFunction( CmdPushConstants ); DECL_vkFunction( CmdResetQueryPool ); DECL_vkFunction( CmdWriteTimestamp ); DECL_vkFunction( CmdPipelineBarrier ); DECL_vkFunction( CmdBlitImage ); DECL_vkFunction( CmdCopyBuffer ); DECL_vkFunction( CmdCopyBufferToImage ); DECL_vkFunction( CmdCopyImage ); DECL_vkFunction( CmdCopyImageToBuffer ); DECL_vkFunction( CmdExecuteCommands ); DECL_vkFunction( CmdResetEvent ); DECL_vkFunction( CmdSetEvent ); DECL_vkFunction( CmdWaitEvents ); DECL_vkFunction( CmdFillBuffer ); #if VK_EXT_debug_utils || VK_EXT_debug_marker # if VK_EXT_debug_utils DECL_vkFunction( SetDebugUtilsObjectNameEXT ); DECL_vkFunction( CmdBeginDebugUtilsLabelEXT ); DECL_vkFunction( CmdEndDebugUtilsLabelEXT ); # endif # if VK_EXT_debug_marker DECL_vkFunction( DebugMarkerSetObjectNameEXT ); DECL_vkFunction( CmdDebugMarkerBeginEXT ); DECL_vkFunction( CmdDebugMarkerEndEXT ); # endif #undef DECL_vkFunction #if VK_EXT_debug_utils || VK_EXT_debug_marker /** *\brief * Begins a command buffer label. *\param[in] labelInfo * The parameters of the label to begin. */ CRG_API void vkCmdBeginDebugBlock( VkCommandBuffer commandBuffer , DebugBlockInfo const & labelInfo )const; /** *\brief * Ends the command label. */ CRG_API void vkCmdEndDebugBlock( VkCommandBuffer commandBuffer )const; #endif CRG_API std::array< float, 4u > getNextRainbowColour()const; CRG_API uint32_t deduceMemoryType( uint32_t typeBits , VkMemoryPropertyFlags requirements )const; private: friend class ResourceHandler; struct ObjectAllocation { std::string type; std::string name; std::string callstack; }; using CallstackCallback = std::function< std::string() >; CallstackCallback m_callstackCallback; std::mutex m_mutex; std::unordered_map< size_t, ObjectAllocation > m_allocated; public: void setCallstackCallback( CallstackCallback callback ) { m_callstackCallback = std::move( callback ); } template< typename ObjectT > static void stRegisterObject( GraphContext & context , std::string const & name , ObjectT object ) { using MyTraits = DebugTypeTraits< ObjectT >; context.doRegisterObject( uint64_t( object ) #if VK_EXT_debug_utils , uint32_t( MyTraits::UtilsValue ) #elif VK_EXT_debug_marker , uint32_t( MyTraits::ReportValue ) #endif , name , MyTraits::Name ); } template< typename ObjectT > static void stRegisterObjectName( GraphContext & context , std::string const & name , ObjectT object ) { using MyTraits = DebugTypeTraits< ObjectT >; context.doRegisterObjectName( uint64_t( object ) #if VK_EXT_debug_utils , uint32_t( MyTraits::UtilsValue ) #elif VK_EXT_debug_marker , uint32_t( MyTraits::ReportValue ) #endif , name ); } template< typename ObjectT > static void stUnregisterObject( GraphContext & context, ObjectT object ) { context.doUnregisterObject( uint64_t( object ) ); } private: #if VK_EXT_debug_utils /** *\brief * Begins a command buffer label. *\param[in] labelInfo * The parameters of the label to begin. */ void doBeginDebugUtilsLabel( VkCommandBuffer commandBuffer , VkDebugUtilsLabelEXT const & labelInfo )const; /** *\brief * Ends the command label. */ void doEndDebugUtilsLabel( VkCommandBuffer commandBuffer )const; #endif #if VK_EXT_debug_marker /** *\brief * Begins a command buffer label. *\param[in] labelInfo * The parameters of the label to begin. */ void doDebugMarkerBegin( VkCommandBuffer commandBuffer , VkDebugMarkerMarkerInfoEXT const & labelInfo )const; /** *\brief * Ends the command label. */ void doDebugMarkerEnd( VkCommandBuffer commandBuffer )const; #endif CRG_API void doRegisterObject( uint64_t object , uint32_t objectType , std::string const & name , std::string const & typeName ); CRG_API void doRegisterObjectName( uint64_t object , uint32_t objectType , std::string const & name ); CRG_API void doUnregisterObject( uint64_t object ); # define crgRegisterObject( Cont, TypeName, Object )\ crg::GraphContext::stRegisterObject( Cont, TypeName, Object ) # define crgUnregisterObject( Cont, Object )\ crg::GraphContext::stUnregisterObject( Cont, Object ) # define crgRegisterObjectName( Cont, TypeName, Object )\ crg::GraphContext::stRegisterObjectName( Cont, TypeName, Object ) #else # define crgRegisterObject( Cont, TypeName, Object ) # define crgUnregisterObject( Cont, Object ) # define crgRegisterObjectName( Cont, TypeName, Object ) #endif }; CRG_API void checkVkResult( VkResult result, char const * const stepName ); CRG_API void checkVkResult( VkResult result, std::string const & stepName ); } ================================================ FILE: include/RenderGraph/GraphNode.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/AttachmentTransition.hpp" #include #include namespace crg { /** *\brief * Base class for all graph nodes *\remarks * It holds a name, the next nodes, and its dependencies from the previous nodes. */ struct GraphNode { enum class Kind { Undefined, Root, FramePass, }; GraphNode & operator=( GraphNode && rhs )noexcept = default; CRG_API virtual ~GraphNode()noexcept; CRG_API GraphNode( GraphNode && rhs )noexcept; CRG_API void attachNode( GraphNode & child ); CRG_API virtual void accept( GraphVisitor * vis )const = 0; void setTransitions( AttachmentTransitions transitions )noexcept { m_transitions = std::move( transitions ); } template< typename NodeT > NodeT const & cast()const noexcept { assert( kind == NodeT::MyKind ); return static_cast< NodeT const & >( *this ); } template< typename NodeT > NodeT & cast()noexcept { assert( kind == NodeT::MyKind ); return static_cast< NodeT & >( *this ); } Kind getKind()const noexcept { return kind; } std::string const & getName()const noexcept { return name; } FramePassGroup const & getGroup()const noexcept { return *group; } uint32_t getId()const noexcept { return id; } GraphAdjacentNodeArray const & getPredecessors()const noexcept { return prev; } GraphAdjacentNodeArray & getPredecessors()noexcept { return prev; } ImageTransitionArray const & getImageTransitions()const noexcept { return m_transitions.imageTransitions; } BufferTransitionArray const & getBufferTransitions()const noexcept { return m_transitions.bufferTransitions; } protected: CRG_API GraphNode( Kind kind , uint32_t id , std::string name , FramePassGroup const & group ); private: GraphNode( GraphNode const & ) = delete; GraphNode & operator=( GraphNode const & ) = delete; private: Kind kind{}; uint32_t id{}; std::string name{}; FramePassGroup const * group; GraphAdjacentNodeArray prev{}; AttachmentTransitions m_transitions{}; }; /** *\brief * Graph node coming from a render pass *\remarks * The node name wil be the FramePass name. */ struct FramePassNode : public GraphNode { static constexpr Kind MyKind = Kind::FramePass; FramePassNode & operator=( FramePassNode && rhs )noexcept = default; FramePassNode( FramePassNode && rhs )noexcept = default; CRG_API ~FramePassNode()noexcept override; CRG_API explicit FramePassNode( FramePass const & pass ); CRG_API void accept( GraphVisitor * vis )const override; FramePass const & getFramePass()const { return *pass; } private: FramePassNode( FramePassNode const & ) = delete; FramePassNode & operator=( FramePassNode const & ) = delete; private: FramePass const * pass; }; /** *\brief * Root node for the graph. *\remarks * Logically, it has no dependency with a previous node. */ struct RootNode : public GraphNode { static constexpr Kind MyKind = Kind::Root; RootNode & operator=( RootNode && rhs )noexcept = default; RootNode( RootNode && rhs )noexcept = default; CRG_API ~RootNode()noexcept override; CRG_API explicit RootNode( FrameGraph const & graph ); CRG_API void accept( GraphVisitor * vis )const override; FrameGraph const & getFrameGraph()const { return *graph; } private: RootNode( RootNode const & ) = delete; RootNode & operator=( RootNode const & ) = delete; private: FrameGraph const * graph; }; CRG_API FramePass const * getFramePass( GraphNode const & node ); inline bool isFramePassNode( GraphNode const & node ) { return node.getKind() == FramePassNode::MyKind; } inline bool isRootNode( GraphNode const & node ) { return node.getKind() == RootNode::MyKind; } inline bool isFramePassNode( ConstGraphAdjacentNode node ) { return node && isFramePassNode( *node ); } inline bool isRootNode( ConstGraphAdjacentNode node ) { return node && isRootNode( *node ); } template< typename NodeT > NodeT const & nodeCast( GraphNode const & node ) { return node.cast< NodeT >(); } template< typename NodeT > NodeT & nodeCast( GraphNode & node ) { return node.cast< NodeT >(); } } ================================================ FILE: include/RenderGraph/GraphVisitor.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/GraphNode.hpp" namespace crg { class GraphVisitor { public: virtual ~GraphVisitor()noexcept = default; CRG_API virtual void visitRootNode( RootNode const * node ) = 0; CRG_API virtual void visitFramePassNode( FramePassNode const * node ) = 0; }; } ================================================ FILE: include/RenderGraph/Hash.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once namespace crg { template< typename T > inline size_t hashCombine( size_t hash , T const & rhs ) { const uint64_t kMul = 0x9ddfea08eb382d69ULL; auto seed = hash; std::hash< T > hasher; uint64_t a = ( hasher( rhs ) ^ seed ) * kMul; a ^= ( a >> 47 ); uint64_t b = ( seed ^ a ) * kMul; b ^= ( b >> 47 ); #pragma warning( push ) #pragma warning( disable: 4068 ) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-warning-option" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wuseless-cast" hash = static_cast< std::size_t >( b * kMul ); #pragma GCC diagnostic pop #pragma clang diagnostic pop #pragma warning( pop ) return hash; } } ================================================ FILE: include/RenderGraph/Id.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/FrameGraphPrerequisites.hpp" namespace crg { template< typename TypeT > struct Id { uint32_t id; TypeT const * data; explicit Id( uint32_t id = 0u , TypeT const * data = nullptr ) : id{ id } , data{ data } { } private: friend bool operator==( Id const & lhs, Id const & rhs ) { return lhs.id == rhs.id; } }; template< typename TypeT > inline bool operator<( Id< TypeT > const & lhs, Id< TypeT > const & rhs ) { return lhs.id < rhs.id; } template< typename TypeT > inline bool operator>( Id< TypeT > const & lhs, Id< TypeT > const & rhs ) { return lhs.id > rhs.id; } } ================================================ FILE: include/RenderGraph/ImageData.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "Id.hpp" namespace crg { /** *\brief * Basic image data, from which images will be created. */ struct ImageData { std::string name; ImageCreateInfo info; explicit ImageData( std::string name = {} , ImageCreateFlags flags = {} , ImageType imageType = {} , PixelFormat format = {} , Extent3D extent = {} , ImageUsageFlags usage = {} , uint32_t mipLevels = 1u , uint32_t arrayLayers = 1u , SampleCount samples = SampleCount::e1 , ImageTiling tiling = ImageTiling::eOptimal , MemoryPropertyFlags memory = MemoryPropertyFlags::eDeviceLocal ) : name{ std::move( name ) } , info{ flags, imageType, format , extent, mipLevels, arrayLayers, samples , tiling, usage, memory } { } private: friend bool operator==( ImageData const & lhs, ImageData const & rhs ) = default; }; } ================================================ FILE: include/RenderGraph/ImageViewData.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "Id.hpp" namespace crg { /** *\brief * Basic image view data, from which views will be created. */ struct ImageViewData { std::string name; ImageId image; ImageViewCreateInfo info; ImageViewIdArray source{}; explicit ImageViewData( std::string name = {} , ImageId image = ImageId{} , ImageViewCreateFlags flags = {} , ImageViewType viewType = {} , PixelFormat format = {} , ImageSubresourceRange subresourceRange = {} ) : name{ std::move( name ) } , image{ std::move( image ) } , info{ flags, viewType, format, subresourceRange } { } private: friend bool operator==( ImageViewData const & lhs, ImageViewData const & rhs ) { return lhs.image == rhs.image && lhs.info == rhs.info; } }; } ================================================ FILE: include/RenderGraph/LayerLayoutStatesHandler.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "FrameGraphPrerequisites.hpp" namespace crg { struct LayerLayoutStatesHandler { LayerLayoutStatesHandler() = default; CRG_API explicit LayerLayoutStatesHandler( LayerLayoutStatesMap const & rhs ); CRG_API void addStates( LayerLayoutStatesHandler const & data ); CRG_API void setLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange , LayoutState const & layoutState ); CRG_API void setLayoutState( crg::ImageViewId view , LayoutState const & layoutState ); CRG_API LayoutState getLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange )const; CRG_API LayoutState getLayoutState( ImageViewId view )const; LayerLayoutStatesMap images; }; } ================================================ FILE: include/RenderGraph/Log.hpp ================================================ /* See LICENSE file in root folder */ #ifndef ___CRG_Log_H___ #define ___CRG_Log_H___ #include "FrameGraphPrerequisites.hpp" #pragma warning( push ) #pragma warning( disable: 4365 ) #pragma warning( disable: 5262 ) #include #include #include #include #pragma warning( pop ) namespace crg { class Logger { public: using LogCallback = void( * )( std::string_view msg, bool newLine )noexcept; public: /** *\brief * Logs a trace message. */ CRG_API static void logTrace( std::string_view message, bool newLine = true )noexcept; /** *\brief * Logs a debug message. */ CRG_API static void logDebug( std::string_view message, bool newLine = true )noexcept; /** *\brief * Logs an info message. */ CRG_API static void logInfo( std::string_view message, bool newLine = true )noexcept; /** *\brief * Logs a warning message. */ CRG_API static void logWarning( std::string_view message, bool newLine = true )noexcept; /** *\brief * Logs an error message. */ CRG_API static void logError( std::string_view message, bool newLine = true )noexcept; /** *\brief * Sets the trace callback. */ CRG_API static void setTraceCallback( LogCallback callback ); /** *\brief * Sets the debug callback. */ CRG_API static void setDebugCallback( LogCallback callback ); /** *\brief * Sets the info callback. */ CRG_API static void setInfoCallback( LogCallback callback ); /** *\brief * Sets the warning callback. */ CRG_API static void setWarningCallback( LogCallback callback ); /** *\brief * Sets the error callback. */ CRG_API static void setErrorCallback( LogCallback callback ); private: Logger(); static Logger & doGetInstance()noexcept; private: LogCallback m_trace; LogCallback m_debug; LogCallback m_info; LogCallback m_warning; LogCallback m_error; }; } #endif ================================================ FILE: include/RenderGraph/PixelFormat.inl ================================================ /* See LICENSE file in root folder */ #ifndef RGPF_ENUM_NON_VALUE # define RGPF_ENUM_NON_VALUE( x, y ) #endif #ifndef RGPF_ENUM_BEGIN # define RGPF_ENUM_BEGIN( x ) #endif #ifndef RGPF_ENUM_END # define RGPF_ENUM_END( x ) #endif #ifndef RGPF_ENUM_VALUE # define RGPF_ENUM_VALUE( name, value, components, alpha, colour, depth, stencil, compressed ) #endif #ifndef RGPF_ENUM_VALUE_COLOR # define RGPF_ENUM_VALUE_COLOR( name, value, components, alpha ) RGPF_ENUM_VALUE( name, value, components, alpha, true, false, false, false ) #endif #ifndef RGPF_ENUM_VALUE_DEPTH_OR_STENCIL # define RGPF_ENUM_VALUE_DEPTH_OR_STENCIL( name, value, components, depth, stencil ) RGPF_ENUM_VALUE( name, value, 1, false, false, depth, stencil, false ) #endif #ifndef RGPF_ENUM_VALUE_DEPTH_STENCIL # define RGPF_ENUM_VALUE_DEPTH_STENCIL( name, value ) RGPF_ENUM_VALUE_DEPTH_OR_STENCIL( name, value, 2, true, true ) #endif #ifndef RGPF_ENUM_VALUE_DEPTH # define RGPF_ENUM_VALUE_DEPTH( name, value ) RGPF_ENUM_VALUE_DEPTH_OR_STENCIL( name, value, 1, true, false ) #endif #ifndef RGPF_ENUM_VALUE_STENCIL # define RGPF_ENUM_VALUE_STENCIL( name, value ) RGPF_ENUM_VALUE_DEPTH_OR_STENCIL( name, value, 1, false, true ) #endif #ifndef RGPF_ENUM_VALUE_COMPRESSED # define RGPF_ENUM_VALUE_COMPRESSED( name, value, components, alpha ) RGPF_ENUM_VALUE( name, value, components, alpha, true, false, false, false ) #endif RGPF_ENUM_BEGIN( PixelFormat ) RGPF_ENUM_VALUE( UNDEFINED, 0, 0, false, false, false, false, false ) RGPF_ENUM_VALUE_COLOR( R4G4_UNORM, 1, 2, false ) RGPF_ENUM_VALUE_COLOR( R4G4B4A4_UNORM, 2, 4, true ) RGPF_ENUM_VALUE_COLOR( B4G4R4A4_UNORM, 3, 4, true ) RGPF_ENUM_VALUE_COLOR( R5G6B5_UNORM, 4, 3, false ) RGPF_ENUM_VALUE_COLOR( B5G6R5_UNORM, 5, 3, false ) RGPF_ENUM_VALUE_COLOR( R5G5B5A1_UNORM, 6, 4, true ) RGPF_ENUM_VALUE_COLOR( B5G5R5A1_UNORM, 7, 4, true ) RGPF_ENUM_VALUE_COLOR( A1R5G5B5_UNORM, 8, 4, true ) RGPF_ENUM_VALUE_COLOR( R8_UNORM, 9, 1, false ) RGPF_ENUM_VALUE_COLOR( R8_SNORM, 10, 1, false ) RGPF_ENUM_VALUE_COLOR( R8_USCALED, 11, 1, false ) RGPF_ENUM_VALUE_COLOR( R8_SSCALED, 12, 1, false ) RGPF_ENUM_VALUE_COLOR( R8_UINT, 13, 1, false ) RGPF_ENUM_VALUE_COLOR( R8_SINT, 14, 1, false ) RGPF_ENUM_VALUE_COLOR( R8_SRGB, 15, 1, false ) RGPF_ENUM_VALUE_COLOR( R8G8_UNORM, 16, 2, false ) RGPF_ENUM_VALUE_COLOR( R8G8_SNORM, 17, 2, false ) RGPF_ENUM_VALUE_COLOR( R8G8_USCALED, 18, 2, false ) RGPF_ENUM_VALUE_COLOR( R8G8_SSCALED, 19, 2, false ) RGPF_ENUM_VALUE_COLOR( R8G8_UINT, 20, 2, false ) RGPF_ENUM_VALUE_COLOR( R8G8_SINT, 21, 2, false ) RGPF_ENUM_VALUE_COLOR( R8G8_SRGB, 22, 2, false ) RGPF_ENUM_VALUE_COLOR( R8G8B8_UNORM, 23, 3, false ) RGPF_ENUM_VALUE_COLOR( R8G8B8_SNORM, 24, 3, false ) RGPF_ENUM_VALUE_COLOR( R8G8B8_USCALED, 25, 3, false ) RGPF_ENUM_VALUE_COLOR( R8G8B8_SSCALED, 26, 3, false ) RGPF_ENUM_VALUE_COLOR( R8G8B8_UINT, 27, 3, false ) RGPF_ENUM_VALUE_COLOR( R8G8B8_SINT, 28, 3, false ) RGPF_ENUM_VALUE_COLOR( R8G8B8_SRGB, 29, 3, false ) RGPF_ENUM_VALUE_COLOR( B8G8R8_UNORM, 30, 3, false ) RGPF_ENUM_VALUE_COLOR( B8G8R8_SNORM, 31, 3, false ) RGPF_ENUM_VALUE_COLOR( B8G8R8_USCALED, 32, 3, false ) RGPF_ENUM_VALUE_COLOR( B8G8R8_SSCALED, 33, 3, false ) RGPF_ENUM_VALUE_COLOR( B8G8R8_UINT, 34, 3, false ) RGPF_ENUM_VALUE_COLOR( B8G8R8_SINT, 35, 3, false ) RGPF_ENUM_VALUE_COLOR( B8G8R8_SRGB, 36, 3, false ) RGPF_ENUM_VALUE_COLOR( R8G8B8A8_UNORM, 37, 4, true ) RGPF_ENUM_VALUE_COLOR( R8G8B8A8_SNORM, 38, 4, true ) RGPF_ENUM_VALUE_COLOR( R8G8B8A8_USCALED, 39, 4, true ) RGPF_ENUM_VALUE_COLOR( R8G8B8A8_SSCALED, 40, 4, true ) RGPF_ENUM_VALUE_COLOR( R8G8B8A8_UINT, 41, 4, true ) RGPF_ENUM_VALUE_COLOR( R8G8B8A8_SINT, 42, 4, true ) RGPF_ENUM_VALUE_COLOR( R8G8B8A8_SRGB, 43, 4, true ) RGPF_ENUM_VALUE_COLOR( B8G8R8A8_UNORM, 44, 4, true ) RGPF_ENUM_VALUE_COLOR( B8G8R8A8_SNORM, 45, 4, true ) RGPF_ENUM_VALUE_COLOR( B8G8R8A8_USCALED, 46, 4, true ) RGPF_ENUM_VALUE_COLOR( B8G8R8A8_SSCALED, 47, 4, true ) RGPF_ENUM_VALUE_COLOR( B8G8R8A8_UINT, 48, 4, true ) RGPF_ENUM_VALUE_COLOR( B8G8R8A8_SINT, 49, 4, true ) RGPF_ENUM_VALUE_COLOR( B8G8R8A8_SRGB, 50, 4, true ) RGPF_ENUM_VALUE_COLOR( A8B8G8R8_UNORM, 51, 4, true ) RGPF_ENUM_VALUE_COLOR( A8B8G8R8_SNORM, 52, 4, true ) RGPF_ENUM_VALUE_COLOR( A8B8G8R8_USCALED, 53, 4, true ) RGPF_ENUM_VALUE_COLOR( A8B8G8R8_SSCALED, 54, 4, true ) RGPF_ENUM_VALUE_COLOR( A8B8G8R8_UINT, 55, 4, true ) RGPF_ENUM_VALUE_COLOR( A8B8G8R8_SINT, 56, 4, true ) RGPF_ENUM_VALUE_COLOR( A8B8G8R8_SRGB, 57, 4, true ) RGPF_ENUM_VALUE_COLOR( A2R10G10B10_UNORM, 58, 4, true ) RGPF_ENUM_VALUE_COLOR( A2R10G10B10_SNORM, 59, 4, true ) RGPF_ENUM_VALUE_COLOR( A2R10G10B10_USCALED, 60, 4, true ) RGPF_ENUM_VALUE_COLOR( A2R10G10B10_SSCALED, 61, 4, true ) RGPF_ENUM_VALUE_COLOR( A2R10G10B10_UINT, 62, 4, true ) RGPF_ENUM_VALUE_COLOR( A2R10G10B10_SINT, 63, 4, true ) RGPF_ENUM_VALUE_COLOR( A2B10G10R10_UNORM, 64, 4, true ) RGPF_ENUM_VALUE_COLOR( A2B10G10R10_SNORM, 65, 4, true ) RGPF_ENUM_VALUE_COLOR( A2B10G10R10_USCALED, 66, 4, true ) RGPF_ENUM_VALUE_COLOR( A2B10G10R10_SSCALED, 67, 4, true ) RGPF_ENUM_VALUE_COLOR( A2B10G10R10_UINT, 68, 4, true ) RGPF_ENUM_VALUE_COLOR( A2B10G10R10_SINT, 69, 4, true ) RGPF_ENUM_VALUE_COLOR( R16_UNORM, 70, 1, false ) RGPF_ENUM_VALUE_COLOR( R16_SNORM, 71, 1, false ) RGPF_ENUM_VALUE_COLOR( R16_USCALED, 72, 1, false ) RGPF_ENUM_VALUE_COLOR( R16_SSCALED, 73, 1, false ) RGPF_ENUM_VALUE_COLOR( R16_UINT, 74, 1, false ) RGPF_ENUM_VALUE_COLOR( R16_SINT, 75, 1, false ) RGPF_ENUM_VALUE_COLOR( R16_SFLOAT, 76, 1, false ) RGPF_ENUM_VALUE_COLOR( R16G16_UNORM, 77, 2, false ) RGPF_ENUM_VALUE_COLOR( R16G16_SNORM, 78, 2, false ) RGPF_ENUM_VALUE_COLOR( R16G16_USCALED, 79, 2, false ) RGPF_ENUM_VALUE_COLOR( R16G16_SSCALED, 80, 2, false ) RGPF_ENUM_VALUE_COLOR( R16G16_UINT, 81, 2, false ) RGPF_ENUM_VALUE_COLOR( R16G16_SINT, 82, 2, false ) RGPF_ENUM_VALUE_COLOR( R16G16_SFLOAT, 83, 2, false ) RGPF_ENUM_VALUE_COLOR( R16G16B16_UNORM, 84, 3, false ) RGPF_ENUM_VALUE_COLOR( R16G16B16_SNORM, 85, 3, false ) RGPF_ENUM_VALUE_COLOR( R16G16B16_USCALED, 86, 3, false ) RGPF_ENUM_VALUE_COLOR( R16G16B16_SSCALED, 87, 3, false ) RGPF_ENUM_VALUE_COLOR( R16G16B16_UINT, 88, 3, false ) RGPF_ENUM_VALUE_COLOR( R16G16B16_SINT, 89, 3, false ) RGPF_ENUM_VALUE_COLOR( R16G16B16_SFLOAT, 90, 3, false ) RGPF_ENUM_VALUE_COLOR( R16G16B16A16_UNORM, 91, 4, true ) RGPF_ENUM_VALUE_COLOR( R16G16B16A16_SNORM, 92, 4, true ) RGPF_ENUM_VALUE_COLOR( R16G16B16A16_USCALED, 93, 4, true ) RGPF_ENUM_VALUE_COLOR( R16G16B16A16_SSCALED, 94, 4, true ) RGPF_ENUM_VALUE_COLOR( R16G16B16A16_UINT, 95, 4, true ) RGPF_ENUM_VALUE_COLOR( R16G16B16A16_SINT, 96, 4, true ) RGPF_ENUM_VALUE_COLOR( R16G16B16A16_SFLOAT, 97, 4, true ) RGPF_ENUM_VALUE_COLOR( R32_UINT, 98, 1, false ) RGPF_ENUM_VALUE_COLOR( R32_SINT, 99, 1, false ) RGPF_ENUM_VALUE_COLOR( R32_SFLOAT, 100, 1, false ) RGPF_ENUM_VALUE_COLOR( R32G32_UINT, 101, 2, false ) RGPF_ENUM_VALUE_COLOR( R32G32_SINT, 102, 2, false ) RGPF_ENUM_VALUE_COLOR( R32G32_SFLOAT, 103, 2, false ) RGPF_ENUM_VALUE_COLOR( R32G32B32_UINT, 104, 3, false ) RGPF_ENUM_VALUE_COLOR( R32G32B32_SINT, 105, 3, false ) RGPF_ENUM_VALUE_COLOR( R32G32B32_SFLOAT, 106, 3, false ) RGPF_ENUM_VALUE_COLOR( R32G32B32A32_UINT, 107, 4, true ) RGPF_ENUM_VALUE_COLOR( R32G32B32A32_SINT, 108, 4, true ) RGPF_ENUM_VALUE_COLOR( R32G32B32A32_SFLOAT, 109, 4, true ) RGPF_ENUM_VALUE_COLOR( R64_UINT, 110, 1, false ) RGPF_ENUM_VALUE_COLOR( R64_SINT, 111, 1, false ) RGPF_ENUM_VALUE_COLOR( R64_SFLOAT, 112, 1, false ) RGPF_ENUM_VALUE_COLOR( R64G64_UINT, 113, 2, false ) RGPF_ENUM_VALUE_COLOR( R64G64_SINT, 114, 2, false ) RGPF_ENUM_VALUE_COLOR( R64G64_SFLOAT, 115, 2, false ) RGPF_ENUM_VALUE_COLOR( R64G64B64_UINT, 116, 3, false ) RGPF_ENUM_VALUE_COLOR( R64G64B64_SINT, 117, 3, false ) RGPF_ENUM_VALUE_COLOR( R64G64B64_SFLOAT, 118, 3, false ) RGPF_ENUM_VALUE_COLOR( R64G64B64A64_UINT, 119, 4, true ) RGPF_ENUM_VALUE_COLOR( R64G64B64A64_SINT, 120, 4, true ) RGPF_ENUM_VALUE_COLOR( R64G64B64A64_SFLOAT, 121, 4, true ) RGPF_ENUM_VALUE_COLOR( B10G11R11_UFLOAT, 122, 3, false ) RGPF_ENUM_VALUE_COLOR( E5B9G9R9_UFLOAT, 123, 3, false ) RGPF_ENUM_VALUE_DEPTH( D16_UNORM, 124 ) RGPF_ENUM_VALUE_DEPTH( X8_D24_UNORM, 125 ) RGPF_ENUM_VALUE_DEPTH( D32_SFLOAT, 126 ) RGPF_ENUM_VALUE_STENCIL( S8_UINT, 127 ) RGPF_ENUM_VALUE_DEPTH_STENCIL( D16_UNORM_S8_UINT, 128 ) RGPF_ENUM_VALUE_DEPTH_STENCIL( D24_UNORM_S8_UINT, 129 ) RGPF_ENUM_VALUE_DEPTH_STENCIL( D32_SFLOAT_S8_UINT, 130 ) RGPF_ENUM_VALUE_COMPRESSED( BC1_RGB_UNORM_BLOCK, 131, 3, false ) // RGB_DXT1 RGPF_ENUM_VALUE_COMPRESSED( BC1_RGB_SRGB_BLOCK, 132, 3, false ) // RGB_DXT1 RGPF_ENUM_VALUE_COMPRESSED( BC1_RGBA_UNORM_BLOCK, 133, 4, true ) // RGBA_DXT1 RGPF_ENUM_VALUE_COMPRESSED( BC1_RGBA_SRGB_BLOCK, 134, 4, true ) // RGBA_DXT1 RGPF_ENUM_VALUE_COMPRESSED( BC2_UNORM_BLOCK, 135, 4, true ) // RGBA_DXT3 RGPF_ENUM_VALUE_COMPRESSED( BC2_SRGB_BLOCK, 136, 4, true ) // RGBA_DXT3 RGPF_ENUM_VALUE_COMPRESSED( BC3_UNORM_BLOCK, 137, 4, true ) // RGBA_DXT5 RGPF_ENUM_VALUE_COMPRESSED( BC3_SRGB_BLOCK, 138, 4, true ) // RGBA_DXT5 RGPF_ENUM_VALUE_COMPRESSED( BC4_UNORM_BLOCK, 139, 1, false ) // R_ATI1N RGPF_ENUM_VALUE_COMPRESSED( BC4_SNORM_BLOCK, 140, 1, false ) // R_ATI1N RGPF_ENUM_VALUE_COMPRESSED( BC5_UNORM_BLOCK, 141, 2, false ) // RG_ATI2N RGPF_ENUM_VALUE_COMPRESSED( BC5_SNORM_BLOCK, 142, 2, false ) // RG_ATI2N RGPF_ENUM_VALUE_COMPRESSED( BC6H_UFLOAT_BLOCK, 143, 3, false ) // RGB_BP RGPF_ENUM_VALUE_COMPRESSED( BC6H_SFLOAT_BLOCK, 144, 3, false ) // RGB_BP RGPF_ENUM_VALUE_COMPRESSED( BC7_UNORM_BLOCK, 145, 4, true ) // RGBA_BP RGPF_ENUM_VALUE_COMPRESSED( BC7_SRGB_BLOCK, 146, 4, true ) // RGBA_BP RGPF_ENUM_VALUE_COMPRESSED( ETC2_R8G8B8_UNORM_BLOCK, 147, 3, false ) RGPF_ENUM_VALUE_COMPRESSED( ETC2_R8G8B8_SRGB_BLOCK, 148, 3, false ) RGPF_ENUM_VALUE_COMPRESSED( ETC2_R8G8B8A1_UNORM_BLOCK, 149, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ETC2_R8G8B8A1_SRGB_BLOCK, 150, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ETC2_R8G8B8A8_UNORM_BLOCK, 151, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ETC2_R8G8B8A8_SRGB_BLOCK, 152, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( EAC_R11_UNORM_BLOCK, 153, 1, false ) RGPF_ENUM_VALUE_COMPRESSED( EAC_R11_SNORM_BLOCK, 154, 1, false ) RGPF_ENUM_VALUE_COMPRESSED( EAC_R11G11_UNORM_BLOCK, 155, 2, false ) RGPF_ENUM_VALUE_COMPRESSED( EAC_R11G11_SNORM_BLOCK, 156, 2, false ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_4x4_UNORM_BLOCK, 157, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_4x4_SRGB_BLOCK, 158, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_5x4_UNORM_BLOCK, 159, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_5x4_SRGB_BLOCK, 160, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_5x5_UNORM_BLOCK, 161, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_5x5_SRGB_BLOCK, 162, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_6x5_UNORM_BLOCK, 163, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_6x5_SRGB_BLOCK, 164, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_6x6_UNORM_BLOCK, 165, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_6x6_SRGB_BLOCK, 166, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_8x5_UNORM_BLOCK, 167, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_8x5_SRGB_BLOCK, 168, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_8x6_UNORM_BLOCK, 169, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_8x6_SRGB_BLOCK, 170, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_8x8_UNORM_BLOCK, 171, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_8x8_SRGB_BLOCK, 172, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_10x5_UNORM_BLOCK, 173, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_10x5_SRGB_BLOCK, 174, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_10x6_UNORM_BLOCK, 175, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_10x6_SRGB_BLOCK, 176, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_10x8_UNORM_BLOCK, 177, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_10x8_SRGB_BLOCK, 178, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_10x10_UNORM_BLOCK, 179, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_10x10_SRGB_BLOCK, 180, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_12x10_UNORM_BLOCK, 181, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_12x10_SRGB_BLOCK, 182, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_12x12_UNORM_BLOCK, 183, 4, true ) RGPF_ENUM_VALUE_COMPRESSED( ASTC_12x12_SRGB_BLOCK, 184, 4, true ) RGPF_ENUM_NON_VALUE( COUNT, 185 ) RGPF_ENUM_END( PixelFormat ) #undef RGPF_ENUM_BEGIN #undef RGPF_ENUM_END #undef RGPF_ENUM_NON_VALUE #undef RGPF_ENUM_VALUE #undef RGPF_ENUM_VALUE_COLOR #undef RGPF_ENUM_VALUE_DEPTH_OR_STENCIL #undef RGPF_ENUM_VALUE_DEPTH_STENCIL #undef RGPF_ENUM_VALUE_DEPTH #undef RGPF_ENUM_VALUE_STENCIL #undef RGPF_ENUM_VALUE_COMPRESSED ================================================ FILE: include/RenderGraph/RecordContext.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "Attachment.hpp" #include "LayerLayoutStatesHandler.hpp" #include #include #include namespace crg { class RecordContext { public: using PassIndexArray = std::vector< uint32_t >; using GraphIndexMap = std::map< FrameGraph const *, PassIndexArray >; using ImplicitAction = std::function< void( RecordContext &, VkCommandBuffer, uint32_t ) >; struct ImplicitImageTransition { RunnablePass const * pass; ImageViewId view; ImplicitAction action; }; struct ImplicitBufferTransition { RunnablePass const * pass; BufferViewId view; ImplicitAction action; }; public: CRG_API explicit RecordContext( ContextResourcesCache & resources ); CRG_API explicit RecordContext( ResourceHandler & handler ); /** *\name States */ //@{ CRG_API void addStates( RecordContext const & data ); /** *\name Pipeline */ //@{ CRG_API void setNextPipelineState( PipelineState const & state , LayerLayoutStatesMap const & imageLayouts ); //@} /** *\name Images */ //@{ CRG_API void setLayoutState( crg::ImageViewId view , LayoutState const & layoutState ); CRG_API LayoutState getLayoutState( ImageViewId view )const; CRG_API void setLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange , LayoutState const & layoutState ); CRG_API LayoutState getLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange )const; CRG_API LayoutState getNextLayoutState( ImageViewId view )const; CRG_API LayoutState getNextLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange )const; CRG_API void registerImplicitTransition( RunnablePass const & pass , crg::ImageViewId view , ImplicitAction action = []( RecordContext const &, VkCommandBuffer, uint32_t ){} ); CRG_API void registerImplicitTransition( RunnablePass const & pass , crg::BufferViewId view , ImplicitAction action = []( RecordContext const &, VkCommandBuffer, uint32_t ){} ); CRG_API void registerImplicitTransition( ImplicitImageTransition transition ); CRG_API void registerImplicitTransition( ImplicitBufferTransition transition ); CRG_API void runImplicitTransition( VkCommandBuffer commandBuffer , uint32_t index , crg::ImageViewId view ); CRG_API void runImplicitTransition( VkCommandBuffer commandBuffer , uint32_t index , crg::BufferViewId view ); //@} /** *\name Buffers */ //@{ CRG_API void setAccessState( BufferViewId buffer , AccessState const & accessState ); CRG_API AccessState getAccessState( BufferViewId view )const; CRG_API void setAccessState( BufferId buffer , BufferSubresourceRange const & subresourceRange , AccessState const & accessState ); CRG_API AccessState const & getAccessState( BufferId buffer , BufferSubresourceRange const & subresourceRange )const; //@} //@} /** *\name Memory barriers */ //@{ /** *\name Images */ //@{ CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , ImageViewId const & view , ImageLayout initialLayout , LayoutState const & wantedState , bool force = false ); CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , ImageId const & image , ImageSubresourceRange const & subresourceRange , ImageLayout initialLayout , LayoutState const & wantedState , bool force = false ); CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , ImageId const & image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange , ImageLayout initialLayout , LayoutState const & wantedState , bool force = false ); CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , ImageViewId const & view , LayoutState const & wantedState , bool force = false ); CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , ImageId const & image , ImageSubresourceRange const & subresourceRange , LayoutState const & wantedState , bool force = false ); CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , ImageId const & image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange , LayoutState const & wantedState , bool force = false ); //@} /** *\name Buffers */ //@{ CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , BufferId buffer , BufferSubresourceRange const & subresourceRange , AccessState const & initialState , AccessState const & wantedState , bool force = false ); CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , BufferViewId view , AccessState const & initialState , AccessState const & wantedState , bool force = false ); CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , BufferId buffer , BufferSubresourceRange const & subresourceRange , AccessState const & wantedState , bool force = false ); CRG_API void memoryBarrier( VkCommandBuffer commandBuffer , BufferViewId view , AccessState const & wantedState , bool force = false ); //@} //@} CRG_API GraphContext & getContext()const; CRG_API ContextResourcesCache & getResources()const; GraphContext * operator->()const { return &getContext(); } CRG_API void copyImage( VkCommandBuffer commandBuffer , uint32_t index , ImageViewId srcView , ImageViewId dstView , Extent2D const & extent , ImageLayout finalLayout = ImageLayout::eUndefined ); CRG_API void blitImage( VkCommandBuffer commandBuffer , uint32_t index , ImageViewId srcView , ImageViewId dstView , Rect2D const & srcRect , Rect2D const & dstRect , FilterMode filter , ImageLayout finalLayout = ImageLayout::eUndefined ); CRG_API void clearAttachment( VkCommandBuffer commandBuffer , ImageViewId dstView , ClearColorValue const & clearValue , ImageLayout finalLayout = ImageLayout::eUndefined ); CRG_API void clearAttachment( VkCommandBuffer commandBuffer , ImageViewId dstView , ClearDepthStencilValue const & clearValue , ImageLayout finalLayout = ImageLayout::eUndefined ); CRG_API void copyBuffer( VkCommandBuffer commandBuffer , uint32_t index , BufferViewId srcView , BufferViewId dstView , DeviceSize srcOffset, DeviceSize dstOffset , DeviceSize size , AccessState const & finalState = {} ); CRG_API void clearBuffer( VkCommandBuffer commandBuffer , BufferViewId dstView , uint32_t clearValue , AccessState const & finalState = {} ); CRG_API static ImplicitAction copyImage( ImageViewId srcView , ImageViewId dstView , Extent2D const & extent , ImageLayout finalLayout = ImageLayout::eUndefined ); CRG_API static ImplicitAction blitImage( ImageViewId srcView , ImageViewId dstView , Rect2D const & srcRect , Rect2D const & dstRect , FilterMode filter , ImageLayout finalLayout = ImageLayout::eUndefined ); CRG_API static ImplicitAction clearAttachment( Attachment const & attach , ImageLayout finalLayout = ImageLayout::eUndefined ); CRG_API static ImplicitAction clearAttachment( ImageViewId view , ClearColorValue const & clearValue , ImageLayout finalLayout = ImageLayout::eUndefined ); CRG_API static ImplicitAction clearAttachment( ImageViewId view , ClearDepthStencilValue const & clearValue , ImageLayout finalLayout = ImageLayout::eUndefined ); CRG_API static ImplicitAction clearBuffer( BufferViewId dstView , AccessState const & finalState = {} ); CRG_API static ImplicitAction clearBuffer( BufferViewId dstView , uint32_t clearValue , AccessState const & finalState = {} ); CRG_API static ImplicitAction copyBuffer( BufferViewId srcView , BufferViewId dstView , DeviceSize srcOffset, DeviceSize dstOffset , DeviceSize size , AccessState const & finalState = {} ); ResourceHandler & getHandler()const { return *m_handler; } PassIndexArray const & getIndexState()const { return m_state; } PipelineState const & getPrevPipelineState()const { return m_prevPipelineState; } PipelineState const & getCurrPipelineState()const { return m_currPipelineState; } PipelineState const & getNextPipelineState()const { return m_nextPipelineState; } private: ResourceHandler * m_handler; ContextResourcesCache * m_resources; LayerLayoutStatesHandler m_images; AccessStateMap m_buffers; std::vector< ImplicitImageTransition > m_implicitImageTransitions; std::vector< ImplicitBufferTransition > m_implicitBufferTransitions; PassIndexArray m_state; PipelineState m_prevPipelineState{}; PipelineState m_currPipelineState{}; PipelineState m_nextPipelineState{}; LayerLayoutStatesHandler m_nextImages; }; } ================================================ FILE: include/RenderGraph/ResourceHandler.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/FrameGraphPrerequisites.hpp" #pragma warning( push ) #pragma warning( disable: 4365 ) #pragma warning( disable: 5262 ) #include #pragma warning( pop ) #include #include namespace crg { struct Sampler { VkSampler sampler; std::string name; }; class ResourceHandler { template< typename ValueT > struct CreatedT { bool created{}; ValueT resource{}; VkDeviceMemory memory{}; }; template< typename ValueT > struct CreatedViewT { bool created{}; ValueT view{}; }; public: ResourceHandler( ResourceHandler const & ) = delete; ResourceHandler( ResourceHandler && )noexcept = delete; ResourceHandler & operator=( ResourceHandler const & ) = delete; ResourceHandler & operator=( ResourceHandler && )noexcept = delete; ResourceHandler() = default; CRG_API ~ResourceHandler()noexcept; CRG_API BufferId createBufferId( BufferData const & img ); CRG_API BufferViewId createViewId( BufferViewData const & view ); CRG_API ImageId createImageId( ImageData const & img ); CRG_API ImageViewId createViewId( ImageViewData const & view ); CRG_API CreatedT< VkBuffer > createBuffer( GraphContext & context , BufferId bufferId ); CRG_API CreatedViewT< VkBufferView > createBufferView( GraphContext & context , BufferViewId viewId ); CRG_API CreatedT< VkImage > createImage( GraphContext & context , ImageId imageId ); CRG_API CreatedViewT< VkImageView > createImageView( GraphContext & context , ImageViewId viewId ); CRG_API VkSampler createSampler( GraphContext & context , std::string const & suffix , SamplerDesc const & samplerDesc ); CRG_API VertexBuffer const * createQuadTriVertexBuffer( GraphContext & context , std::string const & suffix , bool texCoords , Texcoord const & config ); CRG_API void destroyBuffer( GraphContext & context , BufferId bufferId ); CRG_API void destroyBufferView( GraphContext & context , BufferViewId viewId ); CRG_API void destroyImage( GraphContext & context , ImageId imageId ); CRG_API void destroyImageView( GraphContext & context , ImageViewId viewId ); CRG_API void destroySampler( GraphContext & context , VkSampler sampler ); CRG_API void destroyVertexBuffer( GraphContext & context , VertexBuffer const * buffer ); private: mutable std::mutex m_buffersMutex; BufferIdDataOwnerCont m_bufferIds; mutable std::mutex m_bufferViewsMutex; BufferViewIdDataOwnerCont m_bufferViewIds; BufferMemoryMap m_buffers; BufferViewMap m_bufferViews; mutable std::mutex m_imagesMutex; ImageIdDataOwnerCont m_imageIds; mutable std::mutex m_imageViewsMutex; ImageViewIdDataOwnerCont m_imageViewIds; ImageMemoryMap m_images; ImageViewMap m_imageViews; std::mutex m_samplersMutex; std::unordered_map< VkSampler, Sampler > m_samplers; std::mutex m_vertexBuffersMutex; std::unordered_set< VertexBufferPtr > m_vertexBuffers; }; class ContextResourcesCache { public: ContextResourcesCache( ContextResourcesCache const & ) = delete; ContextResourcesCache & operator=( ContextResourcesCache const & ) = delete; ContextResourcesCache( ContextResourcesCache && )noexcept = delete; ContextResourcesCache & operator=( ContextResourcesCache && )noexcept = delete; CRG_API ContextResourcesCache( ResourceHandler & handler , GraphContext & context ); CRG_API ~ContextResourcesCache()noexcept; CRG_API VkBuffer createBuffer( BufferId const & bufferId ); CRG_API VkBuffer createBuffer( BufferId const & bufferId, VkDeviceMemory & memory ); CRG_API VkBufferView createBufferView( BufferViewId const & viewId ); CRG_API bool destroyBuffer( BufferId const & imageId ); CRG_API bool destroyBufferView( BufferViewId const & viewId ); CRG_API VkImage createImage( ImageId const & imageId ); CRG_API VkImage createImage( ImageId const & imageId, VkDeviceMemory & memory ); CRG_API VkImageView createImageView( ImageViewId const & viewId ); CRG_API bool destroyImage( ImageId const & imageId ); CRG_API bool destroyImageView( ImageViewId const & viewId ); CRG_API VkSampler createSampler( SamplerDesc const & samplerDesc ); CRG_API VertexBuffer const & createQuadTriVertexBuffer( bool texCoords , Texcoord const & config ); GraphContext * operator->()const noexcept { return &m_context; } GraphContext & getContext()const noexcept { return m_context; } ResourceHandler & getHandler()const { return m_handler; } private: using VkBufferIdMap = std::map< BufferId, VkBuffer >; using VkBufferViewIdMap = std::map< BufferViewId, VkBufferView >; using VkImageIdMap = std::map< ImageId, VkImage >; using VkImageViewIdMap = std::map< ImageViewId, VkImageView >; ResourceHandler & m_handler; GraphContext & m_context; VkBufferIdMap m_buffers; VkBufferViewIdMap m_bufferViews; VkImageIdMap m_images; VkImageViewIdMap m_imageViews; std::unordered_map< size_t, VkSampler > m_samplers; std::unordered_map< size_t, VertexBuffer const * > m_vertexBuffers; }; class ResourcesCache { public: CRG_API explicit ResourcesCache( ResourceHandler & handler ); CRG_API VkBuffer createBuffer( GraphContext & context , BufferId const & bufferId ); CRG_API VkBuffer createBuffer( GraphContext & context , BufferId const & bufferId , VkDeviceMemory & memory ); CRG_API VkBufferView createBufferView( GraphContext & context , BufferViewId const & viewId ); CRG_API bool destroyBuffer( BufferId const & bufferId ); CRG_API bool destroyBufferView( BufferViewId const & viewId ); CRG_API bool destroyBuffer( GraphContext & context , BufferId const & bufferId ); CRG_API bool destroyBufferView( GraphContext & context , BufferViewId const & viewId ); CRG_API VkImage createImage( GraphContext & context , ImageId const & imageId ); CRG_API VkImage createImage( GraphContext & context , ImageId const & imageId , VkDeviceMemory & memory ); CRG_API VkImageView createImageView( GraphContext & context , ImageViewId const & viewId ); CRG_API bool destroyImage( ImageId const & imageId ); CRG_API bool destroyImageView( ImageViewId const & viewId ); CRG_API bool destroyImage( GraphContext & context , ImageId const & imageId ); CRG_API bool destroyImageView( GraphContext & context , ImageViewId const & viewId ); CRG_API VkSampler createSampler( GraphContext & context , SamplerDesc const & samplerDesc ); CRG_API VertexBuffer const & createQuadTriVertexBuffer( GraphContext & context , bool texCoords , Texcoord const & config ); CRG_API ContextResourcesCache & getContextCache( GraphContext & context ); ResourceHandler & getHandler()const { return m_handler; } private: using ContextCacheMap = std::unordered_map< GraphContext *, ContextResourcesCache >; ResourceHandler & m_handler; ContextCacheMap m_caches; }; } ================================================ FILE: include/RenderGraph/RunnableGraph.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "GraphContext.hpp" #include "FrameGraph.hpp" #include "ResourceHandler.hpp" #include "RunnablePass.hpp" namespace crg { /** *\brief * Tells how the texture coordinates from the vertex buffer are built. */ struct Texcoord { /** *\brief * Tells if the U coordinate of UV must be inverted, thus mirroring vertically the resulting image. */ bool invertU{ false }; /** *\brief * Tells if the U coordinate of UV must be inverted, thus mirroring horizontally the resulting image. */ bool invertV{ false }; }; template<> struct DefaultValueGetterT< Texcoord > { static Texcoord get() { static Texcoord const result{ false, false }; return result; } }; class RunnableGraph { public: /** *\param inputTransitions * Transitions for which the pass is the destination. *\param outputTransitions * Transitions for which the pass is the source. *\param transitions * All transitions. */ CRG_API RunnableGraph( FrameGraph & graph , GraphNodePtrArray nodes , RootNode rootNode , GraphContext & context ); CRG_API ~RunnableGraph()noexcept; CRG_API void record(); CRG_API SemaphoreWaitArray run( VkQueue queue ); CRG_API SemaphoreWaitArray run( SemaphoreWait toWait , VkQueue queue ); CRG_API SemaphoreWaitArray run( SemaphoreWaitArray const & toWait , VkQueue queue ); CRG_API VkBuffer createBuffer( BufferId const & buffer ); CRG_API VkBufferView createBufferView( BufferViewId const & view ); CRG_API VkImage createImage( ImageId const & image ); CRG_API VkImageView createImageView( ImageViewId const & view ); CRG_API VkSampler createSampler( SamplerDesc const & samplerDesc ); CRG_API VertexBuffer const & createQuadTriVertexBuffer( bool texCoords , Texcoord const & config ); CRG_API LayoutState getCurrentLayoutState( RecordContext & context , ImageId image , ImageViewType viewType , ImageSubresourceRange range )const; CRG_API LayoutState getCurrentLayoutState( RecordContext & context , ImageViewId view )const; CRG_API LayoutState getNextLayoutState( RecordContext const & context , crg::RunnablePass const & runnable , ImageViewId view )const; CRG_API LayoutState getOutputLayoutState( ImageViewId view )const; CRG_API VkDescriptorType getDescriptorType( Attachment const & attach )const; CRG_API WriteDescriptorSet getDescriptorWrite( Attachment const & attach, uint32_t binding, uint32_t index = 0u ); CRG_API WriteDescriptorSet getDescriptorWrite( Attachment const & attach, SamplerDesc const & samplerDesc, uint32_t binding, uint32_t index = 0u ); template< typename EnumT > WriteDescriptorSet getDescriptorWriteT( Attachment const & attach, EnumT binding, uint32_t index = 0u ) { return getDescriptorWrite( attach, uint32_t( binding ), index ); } template< typename EnumT > WriteDescriptorSet getDescriptorWriteT( Attachment const & attach, SamplerDesc const & samplerDesc, EnumT binding, uint32_t index = 0u ) { return getDescriptorWrite( attach, samplerDesc, uint32_t( binding ), index ); } ConstGraphAdjacentNode getNodeGraph()const noexcept { return &m_rootNode; } std::string const & getName()const noexcept { return m_graph.getName(); } VkCommandPool getCommandPool()const noexcept { return m_commandPool.object; } VkQueryPool getTimerQueryPool()const noexcept { return m_timerQueries.object; } uint32_t & getTimerQueryOffset()noexcept { return m_timerQueryOffset; } ContextResourcesCache & getResources()noexcept { return m_resources; } Fence & getFence()noexcept { return m_fence; } Fence const & getFence()const noexcept { return m_fence; } FramePassTimer const & getTimer()const noexcept { return m_timer; } FramePassTimer & getTimer()noexcept { return m_timer; } GraphContext & getContext()const noexcept { return m_context; } private: FrameGraph & m_graph; GraphContext & m_context; ContextResourcesCache m_resources; GraphNodePtrArray m_nodes; RootNode m_rootNode; ContextObjectT< VkQueryPool > m_timerQueries; uint32_t m_timerQueryOffset{}; ContextObjectT< VkCommandPool > m_commandPool; std::vector< RunnablePassPtr > m_passes; RecordContext::GraphIndexMap m_states; VkCommandBuffer m_commandBuffer{}; VkSemaphore m_semaphore{}; Fence m_fence; FramePassTimer m_timer; }; } ================================================ FILE: include/RenderGraph/RunnablePass.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/FramePass.hpp" #include "RenderGraph/FramePassTimer.hpp" #include "RenderGraph/RecordContext.hpp" #include "RenderGraph/WriteDescriptorSet.hpp" #include namespace crg { CRG_API void checkUndefinedInput( std::string const & stepName , Attachment const & attach , ImageViewId const & view , ImageLayout currentLayout ); template< typename StrongT, typename ValueT > struct GetValueCallbackT { using CallbackT = std::function< ValueT() >; GetValueCallbackT( GetValueCallbackT && )noexcept = default; GetValueCallbackT & operator=( GetValueCallbackT && )noexcept = default; GetValueCallbackT( GetValueCallbackT const & ) = default; GetValueCallbackT & operator=( GetValueCallbackT const & ) = default; explicit GetValueCallbackT( CallbackT callback = {} ) : m_callback{ std::move( callback ) } { } ValueT operator()()const { return m_callback(); } private: CallbackT m_callback; }; template< typename StrongT, typename ValueT > GetValueCallbackT< StrongT, ValueT > makeValueCallback( std::function< ValueT() > callback ) { return GetValueCallbackT< StrongT, ValueT >{ callback }; } class Fence { public: CRG_API Fence( GraphContext & context , std::string const & name , VkFenceCreateInfo create ); Fence( Fence const & ) = delete; Fence & operator=( Fence const & ) = delete; Fence & operator=( Fence && rhs )noexcept = delete; CRG_API Fence( Fence && rhs )noexcept; CRG_API ~Fence()noexcept; CRG_API void reset(); CRG_API VkResult wait( uint64_t timeout ); VkFence getInternal()const noexcept { return m_fence; } private: GraphContext * m_context; VkFence m_fence{}; bool m_fenceWaited{}; }; namespace ru { struct Config { /** *\param[in] view * The action's target viex. *\param[in] action * The implicit action. */ auto & implicitAction( ImageViewId view , RecordContext::ImplicitAction action ) { implicitImageActions.try_emplace( view, action ); return *this; } /** *\param[in] view * The action's target viex. *\param[in] action * The implicit action. */ auto & implicitAction( BufferViewId view , RecordContext::ImplicitAction action ) { implicitBufferActions.try_emplace( view, action ); return *this; } /** *\param[in] action * The action to run before the pass recording. */ auto & prePassAction( RecordContext::ImplicitAction action ) { prePassActions.emplace_back( action ); return *this; } /** *\param[in] action * The action to run after the pass recording. */ auto & postPassAction( RecordContext::ImplicitAction action ) { postPassActions.emplace_back( action ); return *this; } uint32_t maxPassCount{ 1u }; bool resettable{ false }; std::vector< RecordContext::ImplicitAction > prePassActions{}; std::vector< RecordContext::ImplicitAction > postPassActions{}; std::map< ImageViewId, RecordContext::ImplicitAction > implicitImageActions{}; std::map< BufferViewId, RecordContext::ImplicitAction > implicitBufferActions{}; }; } template<> struct DefaultValueGetterT< ru::Config > { static ru::Config get() { ru::Config const result{ []() { return ru::Config{}; }() }; return result; } }; class RunnablePass { public: struct LayoutTransition { // The layout the view is in, when coming to this pass LayoutState from; // The layout this pass needs the view to be in LayoutState needed; // The layout the view needs to be, out from this pass. LayoutState to; }; struct AccessTransition { // The layout the buffer is in, when coming to this pass AccessState from; // The layout this pass needs the buffer to be in AccessState needed; // The layout the buffer needs to be, out from this pass. AccessState to; }; struct PassIndexT { }; struct PipelineStateT { }; struct EnabledT { }; struct ComputePassT { }; using InitialiseCallback = std::function< void( uint32_t passIndex ) >; using RecordCallback = std::function< void( RecordContext &, VkCommandBuffer, uint32_t ) >; using GetPipelineStateCallback = GetValueCallbackT< PipelineStateT, PipelineState >; using GetPassIndexCallback = GetValueCallbackT< PassIndexT, uint32_t >; using IsEnabledCallback = GetValueCallbackT< EnabledT, bool >; using IsComputePassCallback = GetValueCallbackT< ComputePassT, bool >; struct Callbacks { CRG_API Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState ); CRG_API Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState , RecordCallback record ); CRG_API Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState , RecordCallback record , GetPassIndexCallback getPassIndex ); CRG_API Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState , RecordCallback record , GetPassIndexCallback getPassIndex , IsEnabledCallback isEnabled ); CRG_API Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState , RecordCallback record , GetPassIndexCallback getPassIndex , IsEnabledCallback isEnabled , IsComputePassCallback isComputePass ); InitialiseCallback initialise; GetPipelineStateCallback getPipelineState; RecordCallback record; GetPassIndexCallback getPassIndex; IsEnabledCallback isEnabled; IsComputePassCallback isComputePass; }; static constexpr uint32_t InvalidIndex = ~0u; public: CRG_API RunnablePass( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Callbacks callbacks , ru::Config config = {} ); CRG_API virtual ~RunnablePass()noexcept; /** *\brief * Initialises the pass GPU data for given index. *\param[in] passIndex * The index of the pass to initialise. */ CRG_API void initialise( uint32_t passIndex ); /** *\brief * Records the pass commands into the given command buffer. *\param[in,out] context * Stores the states. *\param[in] commandBuffer * Receives the commands. */ CRG_API uint32_t recordCurrentInto( RecordContext & context , VkCommandBuffer commandBuffer ); /** *\brief * Re-records the pass commands into its command buffer. */ CRG_API uint32_t reRecordCurrent(); /** *\brief * Resets the command buffer to initial state. */ CRG_API bool resetCommandBuffer( uint32_t passIndex ); CRG_API void notifyPassRender(); LayoutState getLayoutState( crg::ImageViewId view )const { return m_imageLayouts.getLayoutState( view ); } bool isEnabled()const { return m_callbacks.isEnabled(); } uint32_t getIndex()const { return isEnabled() ? m_callbacks.getPassIndex() : InvalidIndex; } FramePass const & getPass()const { return m_pass; } RunnableGraph & getGraph()const { return m_graph; } FramePassTimer const & getTimer()const { return m_timer; } FramePassTimer & getTimer() { return m_timer; } uint32_t getMaxPassCount()const { return uint32_t( m_passes.size() ); } PipelineState const & getPipelineState()const { return m_pipelineState; } LayerLayoutStatesMap const & getImageLayouts()const { return m_imageLayouts.images; } protected: struct CommandBuffer { VkCommandBuffer commandBuffer{}; bool recorded{}; }; private: void recordOne( CommandBuffer & enabled , uint32_t index , RecordContext & context ); void recordInto( VkCommandBuffer commandBuffer , uint32_t index , RecordContext & context ); VkCommandBuffer doCreateCommandBuffer( std::string const & suffix ); private: using LayoutTransitionMap = std::map< ImageViewId, LayoutTransition >; using AccessTransitionMap = std::map< BufferViewId, AccessTransition >; struct PassData { PassData( PassData const & ) = delete; PassData & operator=( PassData const & ) = delete; PassData & operator=( PassData && )noexcept = delete; PassData( PassData && rhs )noexcept : graph{ rhs.graph } , context{ rhs.context } , commandBuffer{ std::move( rhs.commandBuffer ) } , semaphore{ std::move( rhs.semaphore ) } , fence{ std::move( rhs.fence ) } , layoutTransitions{ std::move( rhs.layoutTransitions ) } , accessTransitions{ std::move( rhs.accessTransitions ) } , initialised{ rhs.initialised } { rhs.commandBuffer.commandBuffer = {}; rhs.commandBuffer.recorded = {}; rhs.semaphore = {}; rhs.initialised = {}; } PassData( RunnableGraph & graph , GraphContext & context , std::string const & baseName ); ~PassData()noexcept; RunnableGraph & graph; GraphContext & context; CommandBuffer commandBuffer; VkSemaphore semaphore{}; Fence fence; LayoutTransitionMap layoutTransitions; AccessTransitionMap accessTransitions; bool initialised{}; }; private: FramePass const & m_pass; GraphContext & m_context; RunnableGraph & m_graph; Callbacks m_callbacks; ru::Config m_ruConfig; PipelineState m_pipelineState; std::vector< PassData > m_passes; FramePassTimer m_timer; std::vector< RecordContext > m_passContexts; LayerLayoutStatesHandler m_imageLayouts; AccessStateMap m_bufferAccesses; }; template<> struct DefaultValueGetterT< RunnablePass::InitialiseCallback > { static RunnablePass::InitialiseCallback get() { RunnablePass::InitialiseCallback const result{ []( uint32_t ){} }; return result; } }; template<> struct DefaultValueGetterT< RunnablePass::GetPipelineStateCallback > { static RunnablePass::GetPipelineStateCallback get() { RunnablePass::GetPipelineStateCallback const result{ [](){ return PipelineState{ AccessFlags::eShaderRead, PipelineStageFlags::eFragmentShader }; } }; return result; } }; template<> struct DefaultValueGetterT< RunnablePass::RecordCallback > { static RunnablePass::RecordCallback get() { RunnablePass::RecordCallback const result{ []( RecordContext &, VkCommandBuffer, uint32_t ){} }; return result; } }; template<> struct DefaultValueGetterT< RunnablePass::GetPassIndexCallback > { static RunnablePass::GetPassIndexCallback get() { RunnablePass::GetPassIndexCallback const result{ [](){ return 0u; } }; return result; } }; template<> struct DefaultValueGetterT< RunnablePass::IsEnabledCallback > { static RunnablePass::IsEnabledCallback get() { RunnablePass::IsEnabledCallback const result{ [](){ return true; } }; return result; } }; template<> struct DefaultValueGetterT< RunnablePass::IsComputePassCallback > { static RunnablePass::IsComputePassCallback get() { RunnablePass::IsComputePassCallback const result{ [](){ return false; } }; return result; } }; } ================================================ FILE: include/RenderGraph/RunnablePasses/BufferCopy.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePass.hpp" namespace crg { class BufferCopy : public RunnablePass { public: CRG_API BufferCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , DeviceSize copyOffset , DeviceSize copyRange , ru::Config ruConfig = {} , GetPassIndexCallback passIndex = GetPassIndexCallback( [](){ return 0u; } ) , IsEnabledCallback isEnabled = IsEnabledCallback( [](){ return true; } ) ); private: void doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const; private: DeviceSize m_copyOffset; DeviceSize m_copyRange; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/BufferToImageCopy.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePass.hpp" namespace crg { class BufferToImageCopy : public RunnablePass { public: CRG_API BufferToImageCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Offset3D const & copyOffset , Extent3D const & copySize , ru::Config ruConfig = {} , GetPassIndexCallback passIndex = GetPassIndexCallback( [](){ return 0u; } ) , IsEnabledCallback isEnabled = IsEnabledCallback( [](){ return true; } ) ); private: void doRecordInto( RecordContext const & context , VkCommandBuffer commandBuffer , uint32_t index )const; private: VkOffset3D m_copyOffset; VkExtent3D m_copySize; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/ComputePass.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/BufferViewData.hpp" #include "RenderGraph/RunnablePass.hpp" #include "RenderGraph/RunnablePasses/PipelineHolder.hpp" namespace crg { namespace cp { struct GroupCountT { }; using GetGroupCountCallback = GetValueCallbackT< GroupCountT, uint32_t >; template< template< typename ValueT > typename WrapperT > struct ConfigT { /** *\param[in] config * The pipeline program. */ auto & program( VkPipelineShaderStageCreateInfoArray const & config ) { m_baseConfig.program( config ); return *this; } /** *\param[in] config * The pipeline programs. */ auto & programs( std::vector< VkPipelineShaderStageCreateInfoArray > const & config ) { m_baseConfig.programs( config ); return *this; } /** *\param[in] config * The pipeline program creator. */ auto & programCreator( ProgramCreator const & config ) { m_baseConfig.programCreator( config ); return *this; } /** *\param[in] config * The descriptor set layout. */ auto & layout( VkDescriptorSetLayout const & config ) { m_baseConfig.layout( config ); return *this; } /** *\param[in] config * The descriptor set layouts. */ auto & layouts( std::vector< VkDescriptorSetLayout > const & config ) { m_baseConfig.layouts( config ); return *this; } /** *\param[in] config * The push constants range. */ auto & pushConstants( VkPushConstantRange const & config ) { m_baseConfig.pushConstants( config ); return *this; } /** *\param[in] config * The push constants range. */ auto & pushConstants( VkPushConstantRangeArray const & config ) { m_baseConfig.pushConstants( config ); return *this; } /** *\param[in] config * Tells if disabled pass should record render pass begin/end. */ auto & baseConfig( pp::Config const & config ) { m_baseConfig = config; return *this; } /** *\param[in] config * The pass index. */ auto & passIndex( uint32_t const * config ) { m_getPassIndex = [config]() { return *config; }; return *this; } /** *\param[in] config * The enabled control variable. */ auto & enabled( bool const * config ) { m_enabled = config; return *this; } /** *\param[in] config * The pass index callback. */ auto & getPassIndex( RunnablePass::GetPassIndexCallback const & config ) { m_getPassIndex = config; return *this; } /** *\param[in] config * The callback checking the enable status of the pass. */ auto & isEnabled( RunnablePass::IsEnabledCallback const & config ) { m_isEnabled = config; return *this; } /** *\param[in] config * The callback recording the pass. */ auto & recordInto( RunnablePass::RecordCallback const & config ) { m_recordInto = config; return *this; } /** *\param[in] config * The callback initialising the pass. */ auto & initialise( RunnablePass::InitialiseCallback const & config ) { m_initialise = config; return *this; } /** *\param[in] config * The callback ending the pass. */ auto & end( RunnablePass::RecordCallback const & config ) { m_end = config; return *this; } /** *\param[in] config * The X dispatch groups count. */ auto & groupCountX( uint32_t config ) { m_groupCountX = config; return *this; } /** *\param[in] config * The Y dispatch groups count. */ auto & groupCountY( uint32_t config ) { m_groupCountY = config; return *this; } /** *\param[in] config * The Z dispatch groups count. */ auto & groupCountZ( uint32_t config ) { m_groupCountZ = config; return *this; } /** *\param[in] config * The callback to retrieve the X dispatch groups count. */ auto & getGroupCountX( GetGroupCountCallback const & config ) { m_getGroupCountX = config; return *this; } /** *\param[in] config * The callback to retrieve the Y dispatch groups count. */ auto & getGroupCountY( GetGroupCountCallback const & config ) { m_getGroupCountY = config; return *this; } /** *\param[in] config * The callback to retrieve the Z dispatch groups count. */ auto & getGroupCountZ( GetGroupCountCallback const & config ) { m_getGroupCountZ = config; return *this; } /** *\param[in] config * The buffer used during indirect compute. */ auto & indirectBuffer( IndirectBuffer const & config ) { m_indirectBuffer = config; return *this; } pp::ConfigT< WrapperT > m_baseConfig{}; WrapperT< RunnablePass::InitialiseCallback > m_initialise{}; WrapperT< bool const * > m_enabled{}; WrapperT< RunnablePass::IsEnabledCallback > m_isEnabled{}; WrapperT< RunnablePass::GetPassIndexCallback > m_getPassIndex{}; WrapperT< RunnablePass::RecordCallback > m_recordInto{}; WrapperT< RunnablePass::RecordCallback > m_end{}; WrapperT< uint32_t > m_groupCountX{}; WrapperT< uint32_t > m_groupCountY{}; WrapperT< uint32_t > m_groupCountZ{}; WrapperT< GetGroupCountCallback > m_getGroupCountX{}; WrapperT< GetGroupCountCallback > m_getGroupCountY{}; WrapperT< GetGroupCountCallback > m_getGroupCountZ{}; WrapperT< IndirectBuffer > m_indirectBuffer{}; }; template<> struct ConfigT< RawTypeT > { RawTypeT< RunnablePass::InitialiseCallback > initialise{}; RawTypeT< bool const * > enabled{ nullptr }; std::optional< RunnablePass::IsEnabledCallback > isEnabled{}; RawTypeT< RunnablePass::GetPassIndexCallback > getPassIndex{}; RawTypeT< RunnablePass::RecordCallback > recordInto{}; RawTypeT< RunnablePass::RecordCallback > end{}; RawTypeT< uint32_t > groupCountX{ 1u }; RawTypeT< uint32_t > groupCountY{ 1u }; RawTypeT< uint32_t > groupCountZ{ 1u }; std::optional< GetGroupCountCallback > getGroupCountX{}; std::optional< GetGroupCountCallback > getGroupCountY{}; std::optional< GetGroupCountCallback > getGroupCountZ{}; RawTypeT< IndirectBuffer > indirectBuffer{ defaultV< IndirectBuffer > }; }; using Config = ConfigT< std::optional >; using ConfigData = ConfigT< RawTypeT >; } template<> struct DefaultValueGetterT < cp::GetGroupCountCallback > { static cp::GetGroupCountCallback get() { cp::GetGroupCountCallback const result{ []() { return 0u; } }; return result; } }; template<> struct DefaultValueGetterT< cp::Config > { static cp::Config get() { cp::Config const result{ []() { return cp::Config{}; }() }; return result; } }; class ComputePass : public RunnablePass { public: CRG_API ComputePass( FramePass const & pass , GraphContext & context , RunnableGraph & graph , ru::Config const & ruConfig = {} , cp::Config cpConfig = {} ); CRG_API void resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ); CRG_API VkPipelineLayout getPipelineLayout()const; private: void doInitialise( uint32_t index ); uint32_t doGetPassIndex()const; bool doIsEnabled()const; void doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ); void doCreatePipeline( uint32_t index ); private: cp::ConfigData m_cpConfig; PipelineHolder m_pipeline; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/GenerateMipmaps.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePass.hpp" namespace crg { class GenerateMipmaps : public RunnablePass { public: CRG_API GenerateMipmaps( FramePass const & pass , GraphContext & context , RunnableGraph & graph , ImageLayout outputLayout = ImageLayout::eUndefined , ru::Config ruConfig = {} , GetPassIndexCallback passIndex = GetPassIndexCallback( [](){ return 0u; } ) , IsEnabledCallback isEnabled = IsEnabledCallback( [](){ return true; } ) ); private: void doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const; void doProcessImageView( RecordContext & context , VkCommandBuffer commandBuffer , ImageViewId viewId )const; private: LayoutState m_outputLayout; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/ImageBlit.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePass.hpp" namespace crg { class ImageBlit : public RunnablePass { public: CRG_API ImageBlit( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Rect3D const & blitSrc , Rect3D const & blitDst , FilterMode filter , ru::Config ruConfig = {} , GetPassIndexCallback passIndex = GetPassIndexCallback( [](){ return 0u; } ) , IsEnabledCallback isEnabled = IsEnabledCallback( [](){ return true; } ) ); private: void doRecordInto( RecordContext const & context , VkCommandBuffer commandBuffer , uint32_t index )const; private: VkOffset3D m_srcOffset; VkExtent3D m_srcSize; VkOffset3D m_dstOffset; VkExtent3D m_dstSize; FilterMode m_filter; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/ImageCopy.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePass.hpp" namespace crg { class ImageCopy : public RunnablePass { public: CRG_API ImageCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Extent3D const & copySize , ru::Config ruConfig = {} , GetPassIndexCallback passIndex = GetPassIndexCallback( [](){ return 0u; } ) , IsEnabledCallback isEnabled = IsEnabledCallback( [](){ return true; } ) ); CRG_API ImageCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Extent3D const & copySize , ImageLayout finalOutputLayout , ru::Config ruConfig = {} , GetPassIndexCallback passIndex = GetPassIndexCallback( [](){ return 0u; } ) , IsEnabledCallback isEnabled = IsEnabledCallback( [](){ return true; } ) ); private: void doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const; void doRecordMultiToMulti( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const; void doRecordMultiToSingle( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const; void doRecordSingleToMulti( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const; private: VkExtent3D m_copySize; ImageLayout m_finalOutputLayout; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/ImageToBufferCopy.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePass.hpp" namespace crg { class ImageToBufferCopy : public RunnablePass { public: CRG_API ImageToBufferCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Offset3D const & copyOffset , Extent3D const & copySize , ru::Config ruConfig = {} , GetPassIndexCallback passIndex = GetPassIndexCallback( [](){ return 0u; } ) , IsEnabledCallback isEnabled = IsEnabledCallback( [](){ return true; } ) ); private: void doRecordInto( RecordContext const & context , VkCommandBuffer commandBuffer , uint32_t index )const; private: VkOffset3D m_copyOffset; VkExtent3D m_copySize; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/PipelineConfig.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePass.hpp" #include "RenderGraph/WriteDescriptorSet.hpp" #include namespace crg { struct ProgramCreator { uint32_t maxCount{}; std::function< VkPipelineShaderStageCreateInfoArray( uint32_t ) > create; }; template<> struct DefaultValueGetterT< std::vector< VkPipelineShaderStageCreateInfoArray > > { static inline std::vector< VkPipelineShaderStageCreateInfoArray > const value{}; static std::vector< VkPipelineShaderStageCreateInfoArray > get() { return value; } }; template<> struct DefaultValueGetterT< std::vector< VkDescriptorSetLayout > > { static inline std::vector< VkDescriptorSetLayout > const value{}; static std::vector< VkDescriptorSetLayout > get() { return value; } }; template<> struct DefaultValueGetterT< Extent2D > { static inline Extent2D const value{}; static Extent2D get() { return value; } }; template<> struct DefaultValueGetterT< Offset2D > { static inline Offset2D const value{}; static Offset2D get() { return value; } }; template<> struct DefaultValueGetterT< ProgramCreator > { static inline ProgramCreator const value{}; static ProgramCreator get() { return value; } }; template<> struct DefaultValueGetterT< VkPipelineDepthStencilStateCreateInfo > { static inline VkPipelineDepthStencilStateCreateInfo const value{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO , nullptr , 0u , VK_FALSE , VK_FALSE , {} , {} , {} , {} , {} , {} , {} }; static VkPipelineDepthStencilStateCreateInfo get() { return value; } }; template<> struct DefaultValueGetterT< VkPushConstantRangeArray > { static inline VkPushConstantRangeArray const value{}; static VkPushConstantRangeArray get() { return value; } }; template<> struct DefaultValueGetterT< uint32_t const * > { static inline uint32_t const * const value{}; static uint32_t const * get() { return value; } }; template<> struct DefaultValueGetterT< bool const * > { static inline bool const * const value{}; static bool const * get() { return value; } }; namespace pp { template< template< typename ValueT > typename WrapperT > struct ConfigT { ConfigT( ConfigT && )noexcept = default; ConfigT & operator=( ConfigT && )noexcept = default; ConfigT( ConfigT const & ) = default; ConfigT & operator=( ConfigT const & ) = default; explicit ConfigT( WrapperT< std::vector< VkPipelineShaderStageCreateInfoArray > > programs = {} , WrapperT< ProgramCreator > programCreator = {} , WrapperT< std::vector< VkDescriptorSetLayout > > layouts = {} , WrapperT< VkPushConstantRangeArray > pushConstants = {} ) : m_programs{ std::move( programs ) } , m_programCreator{ std::move( programCreator ) } , m_layouts{ std::move( layouts ) } , m_pushConstants{ std::move( pushConstants ) } { } /** *\param[in] config * The pipeline program. */ auto & program( VkPipelineShaderStageCreateInfoArray const & config ) { m_programs = { config }; return *this; } /** *\param[in] config * The pipeline programs. */ auto & programs( std::vector< VkPipelineShaderStageCreateInfoArray > const & config ) { m_programs = config; return *this; } /** *\param[in] config * The pipeline program creator. */ auto & programCreator( ProgramCreator const & config ) { m_programCreator = config; return *this; } /** *\param[in] config * The descriptor set layout. */ auto & layout( VkDescriptorSetLayout const & config ) { m_layouts = { config }; return *this; } /** *\param[in] config * The descriptor set layouts. */ auto & layouts( std::vector< VkDescriptorSetLayout > const & config ) { m_layouts = config; return *this; } /** *\param[in] config * The push constants range. */ auto & pushConstants( VkPushConstantRange const & config ) { pushConstants( VkPushConstantRangeArray{ config } ); return *this; } /** *\param[in] config * The push constants range. */ auto & pushConstants( VkPushConstantRangeArray const & config ) { m_pushConstants = config; return *this; } WrapperT< std::vector< VkPipelineShaderStageCreateInfoArray > > m_programs; WrapperT< ProgramCreator > m_programCreator; WrapperT< std::vector< VkDescriptorSetLayout > > m_layouts; WrapperT< VkPushConstantRangeArray > m_pushConstants; }; using Config = ConfigT< std::optional >; using ConfigData = ConfigT< RawTypeT >; } template<> struct DefaultValueGetterT< pp::Config > { static inline pp::Config const value{ [](){ return pp::Config{}; }() }; static pp::Config get() { return value; } }; } ================================================ FILE: include/RenderGraph/RunnablePasses/PipelineHolder.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePasses/PipelineConfig.hpp" namespace crg { class PipelineHolder { public: PipelineHolder( PipelineHolder const & )noexcept = delete; PipelineHolder & operator=( PipelineHolder const & )noexcept = delete; PipelineHolder( PipelineHolder && )noexcept = delete; PipelineHolder & operator=( PipelineHolder && )noexcept = delete; CRG_API PipelineHolder( FramePass const & pass , GraphContext & context , RunnableGraph & graph , pp::Config config , VkPipelineBindPoint bindingPoint , uint32_t maxPassCount ); CRG_API virtual ~PipelineHolder()noexcept; CRG_API void initialise(); CRG_API void cleanup()noexcept; CRG_API VkPipelineShaderStageCreateInfoArray const & getProgram( uint32_t index ); CRG_API VkPipeline & getPipeline( uint32_t index ); CRG_API void createPipeline( uint32_t index , std::string const & name , VkGraphicsPipelineCreateInfo const & createInfo ); CRG_API void createPipeline( uint32_t index , VkGraphicsPipelineCreateInfo const & createInfo ); CRG_API void createPipeline( uint32_t index , std::string const & name , VkComputePipelineCreateInfo const & createInfo ); CRG_API void createPipeline( uint32_t index , VkComputePipelineCreateInfo const & createInfo ); CRG_API void recordInto( RecordContext const & context , VkCommandBuffer commandBuffer , uint32_t index ); CRG_API void resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config ); CRG_API void resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ); CRG_API void createDescriptorSet( uint32_t index ); VkDescriptorSet getDescriptorSet( uint32_t index ) { createDescriptorSet( index ); return m_descriptorSets[index].set; } VkPipelineLayout getPipelineLayout()const { return m_pipelineLayout; } FramePass const & getPass()const { return m_pass; } GraphContext & getContext()const { return m_context; } private: void doFillDescriptorBindings(); void doCreateDescriptorSetLayout(); void doCreatePipelineLayout(); void doCreateDescriptorPool(); protected: struct DescriptorSet { WriteDescriptorSetArray writes{}; VkDescriptorSet set{}; }; private: FramePass const & m_pass; GraphContext & m_context; RunnableGraph & m_graph; pp::ConfigData m_baseConfig; VkPipelineBindPoint m_bindingPoint; VkDescriptorSetLayoutBindingArray m_descriptorBindings; VkDescriptorSetLayout m_descriptorSetLayout{}; VkPipelineLayout m_pipelineLayout{}; VkDescriptorPool m_descriptorSetPool{}; std::vector< DescriptorSet > m_descriptorSets; std::vector< VkPipeline > m_pipelines{}; }; template< typename BuilderT > class PipelinePassBuilderT { public: /** *\param[in] config * The pipeline program. */ BuilderT & program( VkPipelineShaderStageCreateInfoArray const & config ) { m_baseConfig.programs( { config } ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The pipeline programs. */ BuilderT & programs( std::vector< VkPipelineShaderStageCreateInfoArray > const & config ) { m_baseConfig.programs( config ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The pipeline program creator. */ BuilderT & programCreator( ProgramCreator const & config ) { m_baseConfig.programCreator( config ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The push constants range for the pipeline. */ auto & pushConstants( VkPushConstantRange const & config ) { m_baseConfig.pushConstants( config ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The push constants range for the pipeline. */ auto & pushConstants( VkPushConstantRangeArray const & config ) { m_baseConfig.pushConstants( config ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The descriptor set layouts. */ auto & layout( VkDescriptorSetLayout const & config ) { m_baseConfig.layout( config ); return *this; } /** *\param[in] config * The descriptor set layouts. */ auto & layouts( std::vector< VkDescriptorSetLayout > const & config ) { m_baseConfig.layouts( config ); return *this; } pp::Config const & getBaseConfig()const noexcept { return m_baseConfig; } private: pp::Config m_baseConfig; }; class PipelinePassBuilder : public PipelinePassBuilderT< PipelinePassBuilder > { }; } ================================================ FILE: include/RenderGraph/RunnablePasses/RenderMesh.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePasses/RenderMeshHolder.hpp" #include "RenderGraph/RunnablePasses/RenderPass.hpp" namespace crg { class RenderMesh : public RunnablePass { public: CRG_API RenderMesh( FramePass const & pass , GraphContext & context , RunnableGraph & graph , ru::Config const & ruConfig , rm::Config rmConfig ); ~RenderMesh()noexcept override = default; CRG_API void resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config , uint32_t index ); CRG_API void resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ); private: void doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ); private: RenderMeshHolder m_renderMesh; RenderPassHolder m_renderPass; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/RenderMeshConfig.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnableGraph.hpp" #include "RenderGraph/RunnablePasses/PipelineConfig.hpp" namespace crg { struct PrimitiveCountT { }; struct VertexCountT { }; struct IndexTypeT { }; struct CullModeT { }; using GetPrimitiveCountCallback = GetValueCallbackT< PrimitiveCountT, uint32_t >; using GetVertexCountCallback = GetValueCallbackT< VertexCountT, uint32_t >; using GetIndexTypeCallback = GetValueCallbackT< IndexTypeT, VkIndexType >; using GetCullModeCallback = GetValueCallbackT< CullModeT, VkCullModeFlags >; namespace rm { template< template< typename ValueT > typename WrapperT > struct ConfigT { /** *\param[in] config * The pipeline program. */ auto & program( VkPipelineShaderStageCreateInfoArray const & config ) { m_baseConfig.program( config ); return *this; } /** *\param[in] config * The pipeline programs. */ auto & programs( std::vector< VkPipelineShaderStageCreateInfoArray > const & config ) { m_baseConfig.programs( config ); return *this; } /** *\param[in] config * The pipeline program creator. */ auto & programCreator( ProgramCreator const & config ) { m_baseConfig.programCreator( config ); return *this; } /** *\param[in] config * The descriptor set layout. */ auto & layout( VkDescriptorSetLayout const & config ) { m_baseConfig.layout( config ); return *this; } /** *\param[in] config * The descriptor set layouts. */ auto & layouts( std::vector< VkDescriptorSetLayout > const & config ) { m_baseConfig.layouts( config ); return *this; } /** *\param[in] config * Tells if disabled pass should record render pass begin/end. */ auto & baseConfig( pp::Config const & config ) { m_baseConfig = config; return *this; } /** *\param[in] config * The render area size. */ auto & renderSize( Extent2D const & config ) { m_renderSize = config; return *this; } /** *\param[in] config * The render position. */ auto & renderPosition( Offset2D const & config ) { m_renderPosition = config; return *this; } /** *\param[in] config * The depth stencil state. */ auto & depthStencilState( VkPipelineDepthStencilStateCreateInfo const & config ) { m_depthStencilState = config; return *this; } /** *\param[in] config * The pass index callback. */ auto & getPassIndex( RunnablePass::GetPassIndexCallback const & config ) { m_getPassIndex = config; return *this; } /** *\param[in] config * The callback checking the enable status of the pass. */ auto & isEnabled( RunnablePass::IsEnabledCallback const & config ) { m_isEnabled = config; return *this; } /** *\param[in] config * The callback recording the pass. */ auto & recordInto( RunnablePass::RecordCallback const & config ) { m_recordInto = config; return *this; } /** *\param[in] config * The callback ending the pass. */ auto & end( RunnablePass::RecordCallback const & config ) { m_end = config; return *this; } /** *\param[in] config * The vertex buffer. */ auto & vertexBuffer( VertexBuffer const & config ) { m_vertexBuffer = config; return *this; } /** *\param[in] config * The index buffer. */ auto & indexBuffer( IndexBuffer const & config ) { m_indexBuffer = config; return *this; } /** *\param[in] config * The indirect buffer. */ auto & indirectBuffer( IndirectBuffer const & config ) { m_indirectBuffer = config; return *this; } /** *\param[in] config * The primitive count retrieval callback. */ auto & getPrimitiveCount( GetPrimitiveCountCallback const & config ) { m_getPrimitiveCount = config; return *this; } /** *\param[in] config * The vertex count retrieval callback. */ auto & getVertexCount( GetVertexCountCallback const & config ) { m_getVertexCount = config; return *this; } /** *\param[in] config * The index type retrieval callback. */ auto & getIndexType( GetIndexTypeCallback const & config ) { m_getIndexType = config; return *this; } /** *\param[in] config * The rasterizer cull mode. */ auto & getCullMode( GetCullModeCallback const & config ) { m_getCullMode = config; return *this; } pp::ConfigT< WrapperT > m_baseConfig{}; WrapperT< Offset2D > m_renderPosition{}; WrapperT< VkPipelineDepthStencilStateCreateInfo > m_depthStencilState{}; WrapperT< RunnablePass::GetPassIndexCallback > m_getPassIndex{}; WrapperT< RunnablePass::IsEnabledCallback > m_isEnabled{}; WrapperT< RunnablePass::RecordCallback > m_recordInto{}; WrapperT< RunnablePass::RecordCallback > m_end{}; WrapperT< GetPrimitiveCountCallback > m_getPrimitiveCount{}; WrapperT< GetVertexCountCallback > m_getVertexCount{}; WrapperT< GetIndexTypeCallback > m_getIndexType{}; WrapperT< GetCullModeCallback > m_getCullMode{}; WrapperT< Extent2D > m_renderSize{}; WrapperT< VertexBuffer > m_vertexBuffer{}; WrapperT< IndexBuffer > m_indexBuffer{}; WrapperT< IndirectBuffer > m_indirectBuffer{}; }; template<> struct ConfigT< RawTypeT > { RawTypeT< Offset2D > renderPosition{}; RawTypeT< VkPipelineDepthStencilStateCreateInfo > depthStencilState{}; RawTypeT< RunnablePass::GetPassIndexCallback > getPassIndex{}; RawTypeT< RunnablePass::IsEnabledCallback > isEnabled{}; RawTypeT< RunnablePass::RecordCallback > recordInto{}; RawTypeT< RunnablePass::RecordCallback > end{}; RawTypeT< GetPrimitiveCountCallback > getPrimitiveCount{}; RawTypeT< GetVertexCountCallback > getVertexCount{}; RawTypeT< GetIndexTypeCallback > getIndexType{}; RawTypeT< GetCullModeCallback > getCullMode{}; RawTypeT< VertexBuffer > vertexBuffer{}; RawTypeT< IndexBuffer > indexBuffer{}; RawTypeT< IndirectBuffer > indirectBuffer{ defaultV< IndirectBuffer > }; }; using Config = ConfigT< std::optional >; using ConfigData = ConfigT< RawTypeT >; } template<> struct DefaultValueGetterT< GetPrimitiveCountCallback > { static GetPrimitiveCountCallback get() { GetPrimitiveCountCallback const result{ [](){ return 1u; } }; return result; } }; template<> struct DefaultValueGetterT< GetVertexCountCallback > { static GetVertexCountCallback get() { GetVertexCountCallback const result{ [](){ return 1u; } }; return result; } }; template<> struct DefaultValueGetterT< GetIndexTypeCallback > { static GetIndexTypeCallback get() { GetIndexTypeCallback const result{ [](){ return VK_INDEX_TYPE_UINT32; } }; return result; } }; template<> struct DefaultValueGetterT< GetCullModeCallback > { static GetCullModeCallback get() { GetCullModeCallback const result{ [](){ return VK_CULL_MODE_NONE; } }; return result; } }; } ================================================ FILE: include/RenderGraph/RunnablePasses/RenderMeshHolder.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePasses/RenderMeshConfig.hpp" #include "RenderGraph/RunnablePasses/PipelineHolder.hpp" namespace crg { class RenderMeshHolder { public: CRG_API RenderMeshHolder( FramePass const & pass , GraphContext & context , RunnableGraph & graph , rm::Config config , uint32_t maxPassCount ); CRG_API void initialise( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState , uint32_t index ); CRG_API void cleanup(); CRG_API void resetRenderPass( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState , uint32_t index ); CRG_API void resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config , uint32_t index ); CRG_API void resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ); CRG_API void record( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ); CRG_API void end( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const; CRG_API uint32_t getPassIndex()const; CRG_API bool isEnabled()const; CRG_API Extent2D getRenderSize()const; private: void doPreparePipelineStates( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState ); void doCreatePipeline( uint32_t index ); VkPipelineViewportStateCreateInfo doCreateViewportState( Extent2D const & renderSize , VkViewport & viewport , VkRect2D & scissor )const; private: RunnableGraph & m_graph; rm::ConfigData m_config; PipelineHolder m_pipeline; VkRenderPass m_renderPass{}; Extent2D m_renderSize{}; VkViewport m_viewport{}; VkRect2D m_scissor{}; VkPipelineViewportStateCreateInfo m_vpState{}; VkPipelineInputAssemblyStateCreateInfo m_iaState{}; VkPipelineMultisampleStateCreateInfo m_msState{}; VkPipelineRasterizationStateCreateInfo m_rsState{}; VkPipelineColorBlendStateCreateInfo m_blendState{}; std::vector< VkPipelineColorBlendAttachmentState > m_blendAttachs{}; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/RenderPass.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePasses/RenderPassHolder.hpp" namespace crg { CRG_API VkDescriptorPoolSizeArray getBindingsSizes( VkDescriptorSetLayoutBindingArray const & bindings , uint32_t maxSets ); template< typename VkType, typename LibType > inline std::vector< VkType > makeVkArray( std::vector< LibType > const & input ) { std::vector< VkType > result; result.reserve( input.size() ); for ( auto const & element : input ) { result.emplace_back( static_cast< VkType const & >( element ) ); } return result; } class RenderPass : public RunnablePass { public: template< typename ConfigT, typename BuilderT > friend class RenderQuadBuilderT; struct SubpassContentsT; using GetSubpassContentsCallback = GetValueCallbackT< SubpassContentsT, VkSubpassContents >; struct Callbacks { CRG_API Callbacks( InitialiseCallback initialise , RecordCallback record ); CRG_API Callbacks( InitialiseCallback initialise , RecordCallback record , GetSubpassContentsCallback getSubpassContents ); CRG_API Callbacks( InitialiseCallback initialise , RecordCallback record , GetSubpassContentsCallback getSubpassContents , GetPassIndexCallback getPassIndex ); CRG_API Callbacks( InitialiseCallback initialise , RecordCallback record , GetSubpassContentsCallback getSubpassContents , GetPassIndexCallback getPassIndex , IsEnabledCallback isEnabled ); // RenderPass specifics RunnablePass::InitialiseCallback initialise; RunnablePass::RecordCallback record; GetSubpassContentsCallback getSubpassContents; // Passed to RunnablePass RunnablePass::GetPassIndexCallback getPassIndex; RunnablePass::IsEnabledCallback isEnabled; }; public: CRG_API RenderPass( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Callbacks callbacks , Extent2D size = {} , ru::Config const & ruConfig = {} ); VkRenderPass getRenderPass( uint32_t passIndex )const { return m_holder.getRenderPass( passIndex ); } protected: VkPipelineColorBlendStateCreateInfo doCreateBlendState() { return m_holder.createBlendState(); } VkPipelineColorBlendAttachmentStateArray const & doGetBlendAttachs()const { return m_holder.getBlendAttachs(); } RenderPassHolder const & doGetHolder()const { return m_holder; } private: void doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ); private: Callbacks m_rpCallbacks; RenderPassHolder m_holder; }; template<> struct DefaultValueGetterT< RenderPass::GetSubpassContentsCallback > { static RenderPass::GetSubpassContentsCallback get() { RenderPass::GetSubpassContentsCallback const result{ [](){ return VK_SUBPASS_CONTENTS_INLINE; } }; return result; } }; } ================================================ FILE: include/RenderGraph/RunnablePasses/RenderPassHolder.hpp ================================================ /* See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePass.hpp" namespace crg { class RenderPassHolder { public: struct Entry { Entry( crg::ImageViewId pview , LayoutState pinput , LayoutState poutput ) : view{ std::move( pview ) } , input{ std::move( pinput ) } , output{ std::move( poutput ) } { } crg::ImageViewId view; LayoutState input; LayoutState output; }; public: RenderPassHolder( RenderPassHolder const & )noexcept = delete; RenderPassHolder & operator=( RenderPassHolder const & )noexcept = delete; RenderPassHolder( RenderPassHolder && )noexcept = delete; RenderPassHolder & operator=( RenderPassHolder && )noexcept = delete; CRG_API RenderPassHolder( FramePass const & pass , GraphContext & context , RunnableGraph & graph , uint32_t maxPassCount , Extent2D size = {} ); CRG_API ~RenderPassHolder()noexcept; CRG_API bool initialise( RecordContext & context , crg::RunnablePass const & runnable , uint32_t passIndex ); CRG_API VkRenderPassBeginInfo getBeginInfo( uint32_t index )const; CRG_API void begin( RecordContext & context , VkCommandBuffer commandBuffer , VkSubpassContents subpassContents , uint32_t index ); CRG_API void end( RecordContext & context , VkCommandBuffer commandBuffer ); CRG_API VkPipelineColorBlendStateCreateInfo createBlendState(); CRG_API VkFramebuffer getFramebuffer( uint32_t index )const; VkRenderPass getRenderPass( uint32_t index )const { return m_passes[index].renderPass; } Extent2D const & getRenderSize()const { return m_size; } Rect2D const & getRenderArea( uint32_t index )const { return m_passes[index].renderArea; } std::vector< VkClearValue > const & getClearValues( uint32_t index )const { return m_passes[index].clearValues; } VkPipelineColorBlendAttachmentStateArray const & getBlendAttachs()const { return m_blendAttachs; } private: void doCreateRenderPass( RecordContext & context , crg::RunnablePass const & runnable , PipelineState const & previousState , PipelineState const & nextState , uint32_t passIndex ); void doInitialiseRenderArea( uint32_t index ); VkFramebuffer doCreateFramebuffer( uint32_t passIndex )const; private: struct PassData { VkRenderPass renderPass{}; mutable VkFramebuffer frameBuffer{}; Rect2D renderArea{}; std::vector< Attachment const * > attachments{}; std::vector< VkClearValue > clearValues{}; std::vector< Entry > attaches{}; PipelineState previousState{}; PipelineState nextState{}; void cleanup( crg::GraphContext & context )noexcept; }; FramePass const & m_pass; GraphContext & m_context; RunnableGraph & m_graph; Extent2D m_size; std::vector< PassData > m_passes; PassData const * m_currentPass{}; VkPipelineColorBlendAttachmentStateArray m_blendAttachs; uint32_t m_layers{}; uint32_t m_index{}; uint32_t m_count{ 1u }; }; } ================================================ FILE: include/RenderGraph/RunnablePasses/RenderQuad.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePasses/RenderQuadHolder.hpp" #include "RenderGraph/RunnablePasses/RenderPass.hpp" namespace crg { class RenderQuad : public RunnablePass { public: template< typename ConfigT, typename BuilderT > friend class RenderQuadBuilderT; public: CRG_API RenderQuad( FramePass const & pass , GraphContext & context , RunnableGraph & graph , ru::Config const & ruConfig , rq::Config rqConfig ); CRG_API void resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config , uint32_t index ); CRG_API void resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ); VkPipelineLayout getPipelineLayout()const { return m_renderQuad.getPipelineLayout(); } private: void doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ); uint32_t doGetPassIndex()const; bool doIsEnabled()const; private: RenderQuadHolder m_renderQuad; RenderPassHolder m_renderPass; }; template< typename ConfigT, typename BuilderT > class RenderQuadBuilderT : public PipelinePassBuilderT< BuilderT > { static_assert( std::is_same_v< ConfigT, rq::Config > || std::is_base_of_v< rq::Config, ConfigT > , "RenderQuadBuilderT::ConfigT must derive from crg::rq::Config" ); public: /** *\param[in] config * The texture coordinates configuration. */ auto & texcoordConfig( Texcoord config ) { m_config.texcoordConfig( std::move( config ) ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The render area size. */ auto & renderSize( Extent2D config ) { m_config.renderSize( std::move( config ) ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The render position. */ auto & renderPosition( Offset2D config ) { m_config.renderPosition( std::move( config ) ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The depth stencil state. */ auto & depthStencilState( VkPipelineDepthStencilStateCreateInfo config ) { m_config.depthStencilState( std::move( config ) ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The pass index. */ auto & passIndex( uint32_t const * config ) { m_config.passIndex( config ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The enabled control variable. */ auto & enabled( bool const * config ) { m_config.enabled( config ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The callback checking the enable status of the pass. */ auto & isEnabled( RunnablePass::IsEnabledCallback config ) { m_config.isEnabled( config ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The callback to recording the pass. */ auto & recordInto( RunnablePass::RecordCallback config ) { m_config.recordInto( config ); return static_cast< BuilderT & >( *this ); } /** *\param[in] config * The instances count. */ auto & instances( uint32_t config ) { m_config.instances( config ); return static_cast< BuilderT & >( *this ); } /** *\brief * Creates the RenderQuad. *\param[in] device * The RenderDevice. *\param[in] pass * The render pass. */ std::unique_ptr< RenderQuad > build( FramePass const & pass , GraphContext & context , RunnableGraph & graph , ru::Config ruConfig = {} ) { m_config.baseConfig( std::move( this->getBaseConfig() ) ); return std::make_unique< RenderQuad >( pass , context , graph , std::move( ruConfig ) , m_config ); } private: ConfigT m_config; }; class RenderQuadBuilder : public RenderQuadBuilderT< rq::Config, RenderQuadBuilder > { }; } ================================================ FILE: include/RenderGraph/RunnablePasses/RenderQuadConfig.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/BufferViewData.hpp" #include "RenderGraph/RunnableGraph.hpp" #include "RenderGraph/RunnablePasses/PipelineConfig.hpp" namespace crg { namespace rq { template< template< typename ValueT > typename WrapperT > struct ConfigT { /** *\param[in] config * The pipeline program. */ auto & program( VkPipelineShaderStageCreateInfoArray const & config ) { m_baseConfig.program( config ); return *this; } /** *\param[in] config * The pipeline programs. */ auto & programs( std::vector< VkPipelineShaderStageCreateInfoArray > const & config ) { m_baseConfig.programs( config ); return *this; } /** *\param[in] config * The pipeline program creator. */ auto & programCreator( ProgramCreator const & config ) { m_baseConfig.programCreator( config ); return *this; } /** *\param[in] config * The descriptor set layout. */ auto & layout( VkDescriptorSetLayout const & config ) { m_baseConfig.layout( config ); return *this; } /** *\param[in] config * The descriptor set layouts. */ auto & layouts( std::vector< VkDescriptorSetLayout > const & config ) { m_baseConfig.layouts( config ); return *this; } /** *\param[in] config * The push constants range. */ auto & pushConstants( VkPushConstantRange const & config ) { m_baseConfig.pushConstants( VkPushConstantRangeArray{ config } ); return *this; } /** *\param[in] config * The push constants range. */ auto & pushConstants( VkPushConstantRangeArray const & config ) { m_baseConfig.pushConstants( config ); return *this; } /** *\param[in] config * Tells if disabled pass should record render pass begin/end. */ auto & baseConfig( pp::Config const & config ) { m_baseConfig = config; return *this; } /** *\param[in] config * The texture coordinates configuration. */ auto & texcoordConfig( Texcoord const & config ) { m_texcoordConfig = config; return *this; } /** *\param[in] config * The render area size. */ auto & renderSize( Extent2D const & config ) { m_renderSize = config; return *this; } /** *\param[in] config * The render position. */ auto & renderPosition( Offset2D const & config ) { m_renderPosition = config; return *this; } /** *\param[in] config * The depth stencil state. */ auto & depthStencilState( VkPipelineDepthStencilStateCreateInfo const & config ) { m_depthStencilState = config; return *this; } /** *\param[in] config * The pass index. */ auto & passIndex( uint32_t const * config ) { m_passIndex = config; return *this; } /** *\param[in] config * The enabled control variable. */ auto & enabled( bool const * config ) { m_enabled = config; return *this; } /** *\param[in] config * The callback checking the enable status of the pass. */ auto & isEnabled( RunnablePass::IsEnabledCallback const & config ) { m_isEnabled = config; return *this; } /** *\param[in] config * The callback recording the pass. */ auto & recordInto( RunnablePass::RecordCallback const & config ) { m_recordInto = config; return *this; } /** *\param[in] config * The callback ending the pass. */ auto & end( RunnablePass::RecordCallback const & config ) { m_end = config; return *this; } /** *\param[in] config * The instances count. */ auto & instances( uint32_t config ) { m_instances = config; return *this; } /** *\param[in] config * The indirect buffer. */ auto & indirectBuffer( IndirectBuffer const & config ) { m_indirectBuffer = config; return *this; } pp::ConfigT< WrapperT > m_baseConfig{}; WrapperT< Texcoord > m_texcoordConfig{}; WrapperT< Offset2D > m_renderPosition{}; WrapperT< VkPipelineDepthStencilStateCreateInfo > m_depthStencilState{}; WrapperT< uint32_t const * > m_passIndex{}; WrapperT< bool const * > m_enabled{}; WrapperT< RunnablePass::IsEnabledCallback > m_isEnabled{}; WrapperT< RunnablePass::RecordCallback > m_recordInto{}; WrapperT< RunnablePass::RecordCallback > m_end{}; WrapperT< uint32_t > m_instances{}; WrapperT< Extent2D > m_renderSize{}; WrapperT< IndirectBuffer > m_indirectBuffer{}; }; template<> struct ConfigT< RawTypeT > { RawTypeT< Texcoord > texcoordConfig; RawTypeT< Offset2D > renderPosition; RawTypeT< VkPipelineDepthStencilStateCreateInfo > depthStencilState; RawTypeT< uint32_t const * > passIndex; RawTypeT< bool const * > enabled; std::optional< RunnablePass::IsEnabledCallback > isEnabled; RawTypeT< RunnablePass::RecordCallback > recordInto; RawTypeT< RunnablePass::RecordCallback > end; RawTypeT< uint32_t > m_instances; RawTypeT< IndirectBuffer > indirectBuffer{ BufferViewId{}, 0u }; }; using Config = ConfigT< std::optional >; using ConfigData = ConfigT< RawTypeT >; } template<> struct DefaultValueGetterT< rq::Config > { static rq::Config get() { rq::Config const result{ []() { return rq::Config{}; }() }; return result; } }; } ================================================ FILE: include/RenderGraph/RunnablePasses/RenderQuadHolder.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/RunnablePasses/RenderQuadConfig.hpp" #include "RenderGraph/RunnablePasses/PipelineHolder.hpp" namespace crg { class RenderQuadHolder { public: CRG_API RenderQuadHolder( FramePass const & pass , GraphContext & context , RunnableGraph & graph , rq::Config config , uint32_t maxPassCount ); CRG_API void initialise( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState , uint32_t index ); CRG_API void resetRenderPass( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState , uint32_t index ); CRG_API void resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config , uint32_t index ); CRG_API void resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ); CRG_API void record( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ); CRG_API void end( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const; CRG_API uint32_t getPassIndex()const; CRG_API bool isEnabled()const; VkPipelineVertexInputStateCreateInfo const & getInputState()const { return m_vertexBuffer->inputState; } VkPipelineLayout getPipelineLayout()const { return m_pipeline.getPipelineLayout(); } bool isInitialised()const { return m_vertexBuffer != nullptr; } private: void doPreparePipelineStates( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState ); void doCreatePipeline( uint32_t passIndex ); VkPipelineViewportStateCreateInfo doCreateViewportState( Extent2D const & renderSize , VkViewport & viewport , VkRect2D & scissor )const; private: rq::ConfigData m_config; RunnableGraph & m_graph; PipelineHolder m_pipeline; bool m_useTexCoord{ true }; VertexBuffer const * m_vertexBuffer{}; VkRenderPass m_renderPass{}; Extent2D m_renderSize{}; VkViewport m_viewport{}; VkRect2D m_scissor{}; VkPipelineViewportStateCreateInfo m_vpState{}; VkPipelineInputAssemblyStateCreateInfo m_iaState{}; VkPipelineMultisampleStateCreateInfo m_msState{}; VkPipelineRasterizationStateCreateInfo m_rsState{}; VkPipelineColorBlendStateCreateInfo m_blendState{}; VkPipelineColorBlendAttachmentStateArray m_blendAttachs{}; }; } ================================================ FILE: include/RenderGraph/Signal.hpp ================================================ /* See LICENSE file in root folder */ #pragma once #include #include #include #include #pragma warning( push ) #pragma warning( disable: 4365 ) #pragma warning( disable: 5262 ) #include #pragma warning( pop ) namespace crg { /** *\brief * A connection to a signal. */ template< typename SignalT > class SignalConnection { private: SignalConnection( SignalConnection< SignalT > const & ) = delete; SignalConnection & operator=( SignalConnection< SignalT > const & ) = delete; SignalConnection( uint32_t connection , SignalT * signal ) : m_connection{ connection } , m_signal{ signal } { } public: /** *\name * Construction/Destruction. */ /**\{*/ SignalConnection() : SignalConnection{ 0u, nullptr } { } SignalConnection( SignalConnection< SignalT > && rhs )noexcept : SignalConnection{ 0u, nullptr } { swap( *this, rhs ); } SignalConnection( uint32_t connection, SignalT & signal ) : SignalConnection{ connection, &signal } { signal.addConnection( *this ); } SignalConnection & operator=( SignalConnection< SignalT > && rhs )noexcept { SignalConnection tmp{ std::move( rhs ) }; swap( *this, tmp ); return *this; } ~SignalConnection()noexcept { disconnect(); } /**\}*/ /** *\brief * Disconnects from the signal. */ void disconnect()noexcept { if ( m_signal && m_connection ) { m_signal->disconnect( m_connection ); m_signal->remConnection( *this ); m_signal = nullptr; m_connection = 0u; } } bool isValid()const noexcept { return m_signal && m_connection; } private: static void swap( SignalConnection & lhs, SignalConnection & rhs ) { if ( lhs.m_signal ) lhs.m_signal->remConnection( lhs ); if ( rhs.m_signal ) rhs.m_signal->remConnection( rhs ); std::swap( lhs.m_signal, rhs.m_signal ); std::swap( lhs.m_connection, rhs.m_connection ); if ( lhs.m_signal ) lhs.m_signal->addConnection( lhs ); if ( rhs.m_signal ) rhs.m_signal->addConnection( rhs ); } private: uint32_t m_connection; SignalT * m_signal; }; /** *\brief * Signal implementation. */ template< typename Function > class Signal { friend class SignalConnection< Signal< Function > >; using my_connection = SignalConnection< Signal< Function > >; using my_connection_ptr = my_connection *; using lock_type = std::unique_lock< std::recursive_mutex >; private: Signal( Signal const & )noexcept = delete; Signal & operator=( Signal const & )noexcept = delete; Signal( Signal && )noexcept = delete; Signal & operator=( Signal && )noexcept = delete; public: Signal()noexcept = default; /** *\brief * Destructor. *\remarks * Disconnects all remaining connections. */ ~Signal()noexcept { // SignalConnection::disconnect appelle Signal::remConnection, qui // supprime la connection de m_connections, invalidant ainsi // l'itérateur, donc on ne peut pas utiliser un for_each, ni // un range for loop. lock_type lock( m_mutex ); auto it = m_connections.begin(); while ( it != m_connections.end() ) { ( *it )->disconnect(); it = m_connections.begin(); } } /** *\brief * Connects a new function, called when the signal is emitted. *\param[in] function * The function. *\return * The connection. */ my_connection connect( Function function ) { uint32_t index = uint32_t( m_slots.size() ) + 1u; m_slots.emplace( index, function ); return my_connection{ index, *this }; } /** *\brief * Emits the signal, calling all connected functions. */ void operator()()const { auto it = m_slots.begin(); size_t size = m_slots.size(); size_t index = 0u; while ( it != m_slots.end() ) { it->second(); adjustSlots( size, index, it ); } } /** *\brief * Emits the signal, calling all connected functions. *\param[in] params * The functions parameters. */ template< typename ... Params > void operator()( Params && ... params )const { auto it = m_slots.begin(); size_t size = m_slots.size(); size_t index = 0u; while ( it != m_slots.end() ) { it->second( std::forward< Params >( params )... ); adjustSlots( size, index, it ); } } private: /** *\brief * Disconnects a function. *\param[in] index * The function index. */ void disconnect( uint32_t index )noexcept { auto it = m_slots.find( index ); if ( it != m_slots.end() ) { m_slots.erase( it ); } } /** *\brief * Adds a connection to the list. *\param[in] connection * The connection to add. */ void addConnection( my_connection & connection ) { lock_type lock( m_mutex ); m_connections.insert( &connection ); } /** *\brief * Removes a connection from the list. *\param[in] connection * The connection to remove. */ void remConnection( my_connection & connection )noexcept { lock_type lock( m_mutex ); assert( m_connections.find( &connection ) != m_connections.end() ); m_connections.erase( &connection ); } /** *\brief * Adjusts returned slot iterator. *\param[in,out] size * The expected size, receives the real size. *\param[in,out] index * The current iteration index. */ template< typename IterT > void adjustSlots( size_t & size , size_t & index , IterT & it )const noexcept { if ( size != m_slots.size() ) { // Slots changed by the slot itself. size = m_slots.size(); it = m_slots.begin(); std::advance( it, index ); } else { ++index; ++it; } } private: std::map< uint32_t, Function > m_slots; std::set< my_connection_ptr > m_connections; std::recursive_mutex m_mutex; }; } ================================================ FILE: include/RenderGraph/Version.hpp.in ================================================ /* See LICENSE file in root folder */ #ifndef ___CRG_Version___ #define ___CRG_Version___ #define CRG_VersionMajor ${VERSION_MAJOR} #define CRG_VersionMinor ${VERSION_MINOR} #define CRG_VersionBuild ${VERSION_BUILD} #endif ================================================ FILE: include/RenderGraph/WriteDescriptorSet.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/FrameGraphPrerequisites.hpp" namespace crg { struct WriteDescriptorSet { WriteDescriptorSet( uint32_t dstBinding , uint32_t dstArrayElement , uint32_t descriptorCount , VkDescriptorType descriptorType ) : vk{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, VkDescriptorSet{}, dstBinding, dstArrayElement, descriptorCount, descriptorType, nullptr, nullptr, nullptr } , needsUpdate{ true } { } WriteDescriptorSet( uint32_t dstBinding , uint32_t dstArrayElement , VkDescriptorType descriptorType , VkDescriptorImageInfo imageInfos ) : imageInfo{ 1u, imageInfos } , vk{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, VkDescriptorSet{}, dstBinding, dstArrayElement, uint32_t( this->imageInfo.size() ), descriptorType, nullptr, nullptr, nullptr } , needsUpdate{ true } { } WriteDescriptorSet( uint32_t dstBinding , uint32_t dstArrayElement , VkDescriptorType descriptorType , VkDescriptorImageInfoArray imageInfos ) : imageInfo{ std::move( imageInfos ) } , vk{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, VkDescriptorSet{}, dstBinding, dstArrayElement, uint32_t( this->imageInfo.size() ), descriptorType, nullptr, nullptr, nullptr } , needsUpdate{ true } { } WriteDescriptorSet( VkDescriptorSet set , uint32_t dstBinding , uint32_t dstArrayElement , uint32_t descriptorCount , VkDescriptorType descriptorType , VkDescriptorImageInfo const * imageInfo , VkDescriptorBufferInfo const * bufferInfo , VkBufferView const * texelBufferView ) : vk{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, dstBinding, dstArrayElement, descriptorCount, descriptorType, imageInfo, bufferInfo, texelBufferView } , needsUpdate{ false } { } void update( VkDescriptorSet descriptorSet )const noexcept { if ( needsUpdate ) { vk.dstSet = descriptorSet; vk.pImageInfo = imageInfo.data(); vk.pBufferInfo = bufferInfo.data(); vk.pTexelBufferView = texelBufferView.data(); } } explicit operator VkWriteDescriptorSet const & ()const noexcept { return vk; } VkWriteDescriptorSet const * operator->()const noexcept { return &vk; } VkWriteDescriptorSet * operator->()noexcept { return &vk; } VkDescriptorImageInfoArray imageInfo; VkDescriptorBufferInfoArray bufferInfo; VkDescriptorBufferInfoArray bufferViewInfo; VkBufferViewArray texelBufferView; private: mutable VkWriteDescriptorSet vk; bool needsUpdate; }; } ================================================ FILE: source/CMakeLists.txt ================================================ cmake_minimum_required( VERSION 3.10 ) cmake_policy( VERSION 3.10 ) #-------------------------------------------------------------------------------------------------- # Initial configurations #-------------------------------------------------------------------------------------------------- # Set project name, used in folders and in workspace creation set( MAIN_PROJECT_NAME "RenderGraph" ) # Set project version numbers set( VERSION_MAJOR 2 ) set( VERSION_MINOR 1 ) set( VERSION_BUILD 0 ) set( VERSION_YEAR 2025 ) set( _PROJECT_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}" ) set( _PROJECT_SOVERSION "${VERSION_BUILD}" ) # Used to look for external modules if ( NOT CMAKE_MODULE_PATH ) set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMake ${CMAKE_SOURCE_DIR}/CMake/Modules ${CMAKE_SOURCE_DIR}/CMake/Toolchains ) set( CMAKE_TEMPLATES_DIR ${CMAKE_SOURCE_DIR}/CMake/Templates ) endif () set( CMAKE_POSITION_INDEPENDENT_CODE ON ) set( CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo" CACHE STRING "The configuration types" FORCE ) # Experimental Precompiled headers support for GCC include( PCHSupport ) # Declare the project project( ${MAIN_PROJECT_NAME} ) include( Setup ) include( Project ) include( CompilerVersion ) include( UnitTest ) include( CompilationFlags ) include( AStyleUtils ) include( ExternalDependencies ) include( Coverage ) # Organize projects into folders set_property( GLOBAL PROPERTY USE_FOLDERS ON ) #-------------------------------------------------------------------------------------------------- # Adding include dirs to include search path #-------------------------------------------------------------------------------------------------- set( CRG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} ) set( CRG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR} ) set( CRG_EDITORCONFIG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/.editorconfig ) if ( NOT DEFINED CRG_BUILD_STATIC ) option( CRG_BUILD_STATIC "Build as a static library" ON ) endif () if ( NOT DEFINED CRG_BUILD_TESTS ) option( CRG_BUILD_TESTS "Build RenderGraph test applications" OFF ) endif () if ( NOT DEFINED CRG_UNITY_BUILD ) option( CRG_UNITY_BUILD "Build RenderGraph using Unity (Jumbo) build method" OFF ) endif () set( PROJECTS_UNITY_BUILD ${CRG_UNITY_BUILD} ) if ( MSVC OR NOT "${CMAKE_BUILD_TYPE}" STREQUAL "" ) configure_file( ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Version.hpp.in ${CRG_BINARY_DIR}/include/${PROJECT_NAME}/Version.hpp NEWLINE_STYLE LF ) # RenderGraph library project( RenderGraph ) set( ${PROJECT_NAME}_HDR_FILES ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Attachment.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/AttachmentTransition.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/BufferData.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/BufferViewData.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/DotExport.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Exception.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraph.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphBase.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphEnums.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphFunctions.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphPrerequisites.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FrameGraphStructs.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FramePass.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FramePassGroup.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/FramePassTimer.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/GraphContext.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/GraphNode.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/GraphVisitor.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Hash.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Id.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/ImageData.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/ImageViewData.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/LayerLayoutStatesHandler.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Log.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/PixelFormat.inl ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RecordContext.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/ResourceHandler.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnableGraph.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePass.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/Signal.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/WriteDescriptorSet.hpp ${CRG_BINARY_DIR}/include/${PROJECT_NAME}/Version.hpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/GraphBuilder.hpp ) set( ${PROJECT_NAME}_SRC_FILES ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/Attachment.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/AttachmentTransition.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/DotExport.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FrameGraph.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FrameGraphPrerequisites.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FramePass.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FramePassGroup.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FramePassTimer.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/GraphBuilder.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/GraphContext.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/GraphNode.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/LayerLayoutStatesHandler.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/Log.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RecordContext.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/ResourceHandler.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnableGraph.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePass.cpp ) set( ${PROJECT_NAME}_NVS_FILES ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/FrameGraph.natvis ) source_group( "Header Files" FILES ${${PROJECT_NAME}_HDR_FILES} ) source_group( "Source Files" FILES ${${PROJECT_NAME}_SRC_FILES} ) source_group( "Visualisation Files" FILES ${${PROJECT_NAME}_NVS_FILES} ) set( ${PROJECT_NAME}_FOLDER_HDR_FILES ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/BufferCopy.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/BufferToImageCopy.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/ComputePass.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/GenerateMipmaps.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/ImageBlit.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/ImageCopy.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/ImageToBufferCopy.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/PipelineConfig.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/PipelineHolder.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderPass.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderPassHolder.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderMesh.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderMeshConfig.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderMeshHolder.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderQuad.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderQuadConfig.hpp ${CRG_SOURCE_DIR}/include/${PROJECT_NAME}/RunnablePasses/RenderQuadHolder.hpp ) set( ${PROJECT_NAME}_FOLDER_SRC_FILES ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/BufferCopy.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/BufferToImageCopy.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/ComputePass.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/GenerateMipmaps.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/ImageBlit.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/ImageCopy.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/ImageToBufferCopy.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/PipelineHolder.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderPass.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderPassHolder.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderMesh.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderMeshHolder.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderQuad.cpp ${CRG_SOURCE_DIR}/source/${PROJECT_NAME}/RunnablePasses/RenderQuadHolder.cpp ) set( ${PROJECT_NAME}_SRC_FILES ${${PROJECT_NAME}_SRC_FILES} ${${PROJECT_NAME}_FOLDER_SRC_FILES} ) set( ${PROJECT_NAME}_HDR_FILES ${${PROJECT_NAME}_HDR_FILES} ${${PROJECT_NAME}_FOLDER_HDR_FILES} ) source_group( "Header Files\\RunnablePasses" FILES ${${PROJECT_NAME}_FOLDER_HDR_FILES} ) source_group( "Source Files\\RunnablePasses" FILES ${${PROJECT_NAME}_FOLDER_SRC_FILES} ) if ( ${PROJECTS_UNITY_BUILD} ) file( GLOB ${PROJECT_NAME}_FOLDER_SRC_FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}.dir/Unity/*.cxx ) source_group( "Source Files\\Unity" FILES ${${PROJECT_NAME}_FOLDER_SRC_FILES} ) endif () if ( CRG_BUILD_STATIC ) add_library( ${PROJECT_NAME} STATIC ${${PROJECT_NAME}_HDR_FILES} ${${PROJECT_NAME}_SRC_FILES} ${${PROJECT_NAME}_NVS_FILES} ) target_compile_definitions( ${PROJECT_NAME} PUBLIC CRG_BUILD_STATIC ) else () add_library( ${PROJECT_NAME} SHARED ${${PROJECT_NAME}_HDR_FILES} ${${PROJECT_NAME}_SRC_FILES} ${${PROJECT_NAME}_NVS_FILES} ) set_target_properties( ${PROJECT_NAME} PROPERTIES VERSION ${_PROJECT_VERSION} SOVERSION ${_PROJECT_SOVERSION} ) if ( WIN32 ) target_link_libraries( ${PROJECT_NAME} PUBLIC Dbghelp ) else () target_link_libraries( ${PROJECT_NAME} PRIVATE dl ) endif () endif () add_library( crg::${PROJECT_NAME} ALIAS ${PROJECT_NAME} ) target_add_coverage_flags( ${PROJECT_NAME} ) target_sources( ${PROJECT_NAME} PRIVATE ${CRG_EDITORCONFIG_FILE} ) target_add_compilation_flags( ${PROJECT_NAME} ) target_compile_options( ${PROJECT_NAME} PUBLIC $<$:/Zm300> $<$:-Wno-poison-system-directories> $<$:-Wno-poison-system-directories> ) target_compile_definitions( ${PROJECT_NAME} PUBLIC CRG_VERSION_MAJOR=${VERSION_MAJOR} CRG_VERSION_MINOR=${VERSION_MINOR} CRG_VERSION_BUILD=${VERSION_BUILD} ) target_include_directories( ${PROJECT_NAME} PUBLIC $ $ $ $ ) find_package( VulkanHeaders CONFIG ) target_link_libraries( ${PROJECT_NAME} PRIVATE Vulkan::Headers ) message( STATUS "PROJECTS_UNITY_BUILD [${PROJECTS_UNITY_BUILD}]" ) set_target_properties( ${PROJECT_NAME} PROPERTIES CXX_STANDARD 20 FOLDER "${CRG_BASE_DIR}/Core" DEBUG_POSTFIX "d" UNITY_BUILD "${PROJECTS_UNITY_BUILD}" ) install( TARGETS ${PROJECT_NAME} COMPONENT ${PROJECT_NAME} EXPORT ${PROJECT_NAME} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${CRG_BINARY_DIR}/include/${PROJECT_NAME}/Version.hpp DESTINATION include/${PROJECT_NAME} COMPONENT ${PROJECT_NAME} CONFIGURATIONS Release ) target_install_headers( ${PROJECT_NAME} ${CRG_SOURCE_DIR}/include/${PROJECT_NAME} ) install( EXPORT ${PROJECT_NAME} COMPONENT ${PROJECT_NAME} FILE ${PROJECT_NAME}Config.cmake NAMESPACE crg:: DESTINATION share/${PROJECT_NAME} ) include(CMakePackageConfigHelpers) write_basic_package_version_file( ${PROJECT_NAME}ConfigVersion.cmake VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_BUILD} COMPATIBILITY AnyNewerVersion ) if ( CRG_BUILD_TESTS ) add_subdirectory( test ) endif () else() message( SEND_ERROR "Please select a build type (Debug or Release)" ) endif() ================================================ FILE: source/RenderGraph/Attachment.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/Attachment.hpp" #include "RenderGraph/BufferData.hpp" #include "RenderGraph/BufferViewData.hpp" #include "RenderGraph/Exception.hpp" #include "RenderGraph/ImageData.hpp" #include "RenderGraph/ImageViewData.hpp" #include "RenderGraph/WriteDescriptorSet.hpp" #include #include namespace crg { //********************************************************************************************* BufferAttachment::BufferAttachment( BufferViewIdArray views ) : buffers{ std::move( views ) } , flags{ FlagKind( buffers.front().data->info.format == PixelFormat::eUNDEFINED ? Flag::None : Flag::View ) } { } BufferAttachment::BufferAttachment( FlagKind pflags , BufferViewIdArray views , AccessState access ) : buffers{ std::move( views ) } , flags{ FlagKind( pflags | FlagKind( buffers.front().data->info.format == PixelFormat::eUNDEFINED ? Flag::None : Flag::View ) ) } , wantedAccess{ std::move( access ) } { } BufferViewId BufferAttachment::buffer( uint32_t index )const { return buffers.size() == 1u ? buffers.front() : buffers[index]; } AccessFlags BufferAttachment::getAccessMask( bool isInput , bool isOutput )const { AccessFlags result{ 0u }; if ( isTransition() ) { result = wantedAccess.access; } else if ( isStorage() ) { if ( isInput ) { result |= AccessFlags::eShaderRead; } if ( isOutput ) { result |= AccessFlags::eShaderWrite; } } else if ( isTransfer() ) { if ( isInput ) { result |= AccessFlags::eTransferRead; } if ( isOutput ) { result |= AccessFlags::eTransferWrite; } } else { result |= AccessFlags::eShaderRead; } return result; } PipelineStageFlags BufferAttachment::getPipelineStageFlags( bool isCompute )const { PipelineStageFlags result{ 0u }; if ( isTransition() ) { result = wantedAccess.pipelineStage; } else if ( isTransfer() ) { result |= PipelineStageFlags::eTransfer; } else if ( isCompute ) { result |= PipelineStageFlags::eComputeShader; } else { result |= PipelineStageFlags::eFragmentShader; } return result; } uint32_t BufferAttachment::getBufferCount()const { return uint32_t( buffers.size() ); } //********************************************************************************************* ImageAttachment::ImageAttachment( ImageViewIdArray views ) : views{std::move( views ) } { } ImageAttachment::ImageAttachment( FlagKind flags , ImageViewIdArray views , AttachmentLoadOp loadOp , AttachmentStoreOp storeOp , AttachmentLoadOp stencilLoadOp , AttachmentStoreOp stencilStoreOp , ClearValue clearValue , PipelineColorBlendAttachmentState blendState , ImageLayout wantedLayout ) : views{ std::move( views ) } , loadOp{ loadOp } , storeOp{ storeOp } , stencilLoadOp{ stencilLoadOp } , stencilStoreOp{ stencilStoreOp } , clearValue{ std::move( clearValue ) } , blendState{ std::move( blendState ) } , wantedLayout{ wantedLayout } , flags{ flags } { assert( ( checkFlag( getAspectFlags( view() ), ImageAspectFlags::eColor ) && isColourFormat( getFormat( view() ) ) ) || ( checkFlag( getAspectFlags( view() ), ImageAspectFlags::eDepthStencil ) && isDepthStencilFormat( getFormat( view() ) ) ) || ( checkFlag( getAspectFlags( view() ), ImageAspectFlags::eDepth ) && isDepthFormat( getFormat( view() ) ) ) || ( checkFlag( getAspectFlags( view() ), ImageAspectFlags::eStencil ) && isStencilFormat( getFormat( view() ) ) ) ); assert( ( !isSampledView() && !isTransitionView() && !isTransferView() ) || ( ( this->loadOp == AttachmentLoadOp::eDontCare ) && ( this->storeOp == AttachmentStoreOp::eDontCare ) && ( this->stencilLoadOp == AttachmentLoadOp::eDontCare ) && ( this->stencilStoreOp == AttachmentStoreOp::eDontCare ) ) ); } ImageViewId ImageAttachment::view( uint32_t index )const { return views.size() == 1u ? views.front() : views[index]; } ImageLayout ImageAttachment::getImageLayout( bool separateDepthStencilLayouts , bool isInput , bool isOutput )const { auto result = ImageLayout::eReadOnly; if ( isSampledView() ) { result = ImageLayout::eShaderReadOnly; } else if ( isTransitionView() ) { result = wantedLayout; } else if ( isStorageView() ) { result = ImageLayout::eGeneral; } else if ( isTransferView() ) { if ( isOutput ) result = ImageLayout::eTransferDst; else if ( isInput ) result = ImageLayout::eTransferSrc; } else if ( isColourTarget() ) { result = ImageLayout::eColorAttachment; } #if VK_KHR_separate_depth_stencil_layouts else if ( separateDepthStencilLayouts ) { if ( isDepthStencilTarget() ) { if ( isOutput ) result = ImageLayout::eDepthStencilAttachment; else if ( isInput ) result = ImageLayout::eDepthStencilReadOnly; } else if ( isStencilTarget() ) { if ( isOutput && isStencilOutputTarget() ) result = ImageLayout::eStencilAttachment; else if ( isInput && isStencilInputTarget() ) result = ImageLayout::eStencilReadOnly; } else if ( isDepthTarget() ) { if ( isOutput ) result = ImageLayout::eDepthAttachment; else if ( isInput ) result = ImageLayout::eDepthReadOnly; } } else #endif { if ( isOutput && ( isDepthTarget() || isStencilOutputTarget() ) ) { result = ImageLayout::eDepthStencilAttachment; } else if ( isInput && ( isDepthTarget() || isStencilInputTarget() ) ) { result = ImageLayout::eDepthStencilReadOnly; } } return result; } AccessFlags ImageAttachment::getAccessMask( bool isInput , bool isOutput )const { AccessFlags result{ 0u }; if ( isSampledView() ) { result |= AccessFlags::eShaderRead; } else if ( isTransitionView() ) { result |= crg::getAccessMask( wantedLayout ); } else if ( isStorageView() ) { if ( isInput ) result |= AccessFlags::eShaderRead; if ( isOutput ) result |= AccessFlags::eShaderWrite; } else if ( isTransferView() ) { if ( isInput ) result |= AccessFlags::eTransferRead; if ( isOutput ) result |= AccessFlags::eTransferWrite; } else if ( isDepthTarget() || isStencilTarget() ) { if ( isInput ) result |= AccessFlags::eDepthStencilAttachmentRead; if ( isOutput ) result |= AccessFlags::eDepthStencilAttachmentWrite; } else { if ( isInput ) result |= AccessFlags::eColorAttachmentRead; if ( isOutput ) result |= AccessFlags::eColorAttachmentWrite; } return result; } PipelineStageFlags ImageAttachment::getPipelineStageFlags( bool isCompute )const { PipelineStageFlags result{ 0u }; if ( isSampledView() ) { result |= PipelineStageFlags::eFragmentShader; } else if ( isTransitionView() ) { result |= getStageMask( wantedLayout ); } else if ( isStorageView() ) { if ( isCompute ) result |= PipelineStageFlags::eComputeShader; else result |= PipelineStageFlags::eFragmentShader; } else if ( isTransferView() ) { result |= PipelineStageFlags::eTransfer; } else if ( isDepthTarget() || isStencilTarget() ) { result |= PipelineStageFlags::eLateFragmentTests; } else { result |= PipelineStageFlags::eColorAttachmentOutput; } return result; } //********************************************************************************************* Attachment::Attachment( Attachment const & rhs ) : pass{ rhs.pass } , name{ rhs.name } , imageAttach{ rhs.imageAttach } , bufferAttach{ rhs.bufferAttach } , flags{ rhs.flags } { } Attachment & Attachment::operator=( Attachment const & rhs ) { pass = rhs.pass; name = rhs.name; imageAttach = rhs.imageAttach; bufferAttach = rhs.bufferAttach; flags = rhs.flags; return *this; } Attachment::Attachment( ImageViewId view , Attachment const & origin ) : pass{ origin.pass } , name{ origin.name + view.data->name } , imageAttach{ origin.imageAttach } , flags{ origin.flags } { imageAttach.views = { view }; } Attachment::Attachment( BufferViewId view , Attachment const & origin ) : pass{ origin.pass } , name{ origin.name + view.data->name } , bufferAttach{ origin.bufferAttach } , flags{ origin.flags } { bufferAttach.buffers = { view }; } Attachment::Attachment( ImageViewIdArray views ) : imageAttach{ std::move( views ) } , flags{ FlagKind( Attachment::Flag::Image ) } { } Attachment::Attachment( BufferViewIdArray views ) : bufferAttach{ std::move( views ) } , flags{ FlagKind( Attachment::Flag::Buffer ) } { } Attachment::Attachment( FlagKind pflags , FramePass const & framePass , std::string name , ImageAttachment::FlagKind imageFlags , ImageViewIdArray views , AttachmentLoadOp loadOp , AttachmentStoreOp storeOp , AttachmentLoadOp stencilLoadOp , AttachmentStoreOp stencilStoreOp , ClearValue clearValue , PipelineColorBlendAttachmentState blendState , ImageLayout wantedLayout , Token ) : pass{ &framePass } , name{ std::move( name ) } , imageAttach{ imageFlags , std::move( views ) , loadOp , storeOp , stencilLoadOp , stencilStoreOp , std::move( clearValue ) , std::move( blendState ) , wantedLayout } , flags{ FlagKind( pflags | FlagKind( Flag::Image ) | ( loadOp == AttachmentLoadOp::eLoad ? FlagKind( Flag::Input ) : FlagKind( Flag::None ) ) | ( storeOp == AttachmentStoreOp::eStore ? FlagKind( Flag::Output ) : FlagKind( Flag::None ) ) | ( stencilLoadOp == AttachmentLoadOp::eLoad ? FlagKind( Flag::Input ) : FlagKind( Flag::None ) ) | ( stencilStoreOp == AttachmentStoreOp::eStore ? FlagKind( Flag::Output ) : FlagKind( Flag::None ) ) ) } { } Attachment::Attachment( FlagKind pflags , FramePass const & framePass , std::string name , BufferAttachment::FlagKind bufferFlags , BufferViewIdArray views , AccessState wantedAccess , Token ) : pass{ &framePass } , name{ std::move( name ) } , bufferAttach{ bufferFlags, std::move( views ), std::move( wantedAccess ) } , flags{ FlagKind( pflags | FlagKind( Flag::Buffer ) ) } { } Attachment::Attachment( FlagKind pflags , std::string name , FramePass const * framePass , ImageAttachment attach , Token ) : pass{ framePass } , name{ std::move( name ) } , imageAttach{ std::move( attach ) } , flags{ pflags } { } Attachment::Attachment( FlagKind pflags , std::string name , FramePass const * framePass , BufferAttachment attach , Token ) : pass{ framePass } , name{ std::move( name ) } , bufferAttach{ std::move( attach ) } , flags{ pflags } { } uint32_t Attachment::getViewCount()const { return isImage() ? imageAttach.getViewCount() : uint32_t{}; } uint32_t Attachment::getBufferCount()const { return isBuffer() ? bufferAttach.getBufferCount() : uint32_t{}; } ImageViewId Attachment::view( uint32_t index )const { return isImage() ? imageAttach.view( index ) : ImageViewId{}; } BufferViewId Attachment::buffer( uint32_t index )const { return isBuffer() ? bufferAttach.buffer( index ) : BufferViewId{}; } ImageLayout Attachment::getImageLayout( bool separateDepthStencilLayouts )const { assert( isImage() ); return imageAttach.getImageLayout( separateDepthStencilLayouts , isInput() , isOutput() ); } AccessFlags Attachment::getAccessMask()const { if ( isImage() ) { return imageAttach.getAccessMask( isInput() , isOutput() ); } return bufferAttach.getAccessMask( isInput() , isOutput() ); } PipelineStageFlags Attachment::getPipelineStageFlags( bool isCompute )const { if ( isImage() ) { return imageAttach.getPipelineStageFlags( isCompute ); } return bufferAttach.getPipelineStageFlags( isCompute ); } Attachment const * Attachment::getSource( uint32_t index )const { if ( index > 0 && index >= source.size() ) CRG_Exception( "Invalid index" ); if ( source.empty() ) return this; return source[index].attach.get(); } void Attachment::initSources() { for ( uint32_t index = 0u; index < source.size(); ++index ) { Source & attachSource = source[index]; if ( isImage() ) { attachSource.attach = std::make_unique< Attachment >( flags , name + std::to_string( index ) , attachSource.pass , *attachSource.imageAttach , Token{} ); attachSource.imageAttach = &attachSource.attach->imageAttach; } else { attachSource.attach = std::make_unique< Attachment >( flags , name + std::to_string( index ) , attachSource.pass , *attachSource.bufferAttach , Token{} ); attachSource.bufferAttach = &attachSource.attach->bufferAttach; } } } //********************************************************************************************* } ================================================ FILE: source/RenderGraph/AttachmentTransition.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/AttachmentTransition.hpp" #include "RenderGraph/FramePass.hpp" #include "RenderGraph/LayerLayoutStatesHandler.hpp" #include namespace crg { namespace attTran { template< typename TransitionT > std::vector< TransitionT > mergeIdenticalTransitionsT( std::vector< TransitionT > transitions ) { std::vector< TransitionT > result; for ( auto & transition : transitions ) { if ( auto it = std::find( result.begin(), result.end(), transition ); it == result.end() ) { result.push_back( transition ); } } return result; } } AttachmentTransitions mergeIdenticalTransitions( AttachmentTransitions transitions ) { AttachmentTransitions result{}; result.imageTransitions = attTran::mergeIdenticalTransitionsT( std::move( transitions.imageTransitions ) ); result.bufferTransitions = attTran::mergeIdenticalTransitionsT( std::move( transitions.bufferTransitions ) ); return result; } } ================================================ FILE: source/RenderGraph/BuilderCommon.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #pragma once #include "RenderGraph/FrameGraphPrerequisites.hpp" #include namespace crg::builder { using FramePassSet = std::set< FramePass const * >; template< typename TypeT > void filter( std::vector< TypeT > const & inputs , std::function< bool( TypeT const & ) > filterFunc , std::function< void( TypeT const & ) > trueFunc , std::function< void( TypeT const & ) > falseFunc = nullptr ) { for ( auto & input : inputs ) { if ( filterFunc( input ) ) { trueFunc( input ); } else if ( falseFunc ) { falseFunc( input ); } } } template< typename TypeT > void filter( std::vector< TypeT > & inputs , std::function< bool( TypeT & ) > filterFunc , std::function< void( TypeT & ) > trueFunc , std::function< void( TypeT & ) > falseFunc = nullptr ) { for ( auto & input : inputs ) { if ( filterFunc( input ) ) { trueFunc( input ); } else if ( falseFunc ) { falseFunc( input ); } } } } ================================================ FILE: source/RenderGraph/DotExport.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/DotExport.hpp" #include "RenderGraph/FrameGraph.hpp" #include "RenderGraph/FramePass.hpp" #include "RenderGraph/GraphVisitor.hpp" #include "RenderGraph/RunnableGraph.hpp" #include #include #include #include #include #pragma warning( push ) #pragma warning( disable: 5262 ) #include #include #include #pragma warning( pop ) namespace crg::dot { namespace dotexp { template < typename char_type, char_type fill_char = ' ', typename traits = std::char_traits< char_type > > struct BasicIndentBuffer : public std::basic_streambuf< char_type, traits > { public: using int_type = typename traits::int_type; using pos_type = typename traits::pos_type; using off_type = typename traits::off_type; private: BasicIndentBuffer( const BasicIndentBuffer< char_type, fill_char, traits > & ) = delete; BasicIndentBuffer< char_type, fill_char, traits > & operator =( const BasicIndentBuffer< char_type, fill_char, traits > & ) = delete; public: explicit BasicIndentBuffer( std::basic_streambuf< char_type, traits > * sbuf ) : m_sbuf{ sbuf } , m_set{ true } { } int indent() const { return m_count; } void indent( long i ) { m_count += i; m_count = std::max( 0L, m_count ); } std::streambuf * sbuf() const { return m_sbuf; } private: int_type overflow( int_type c = traits::eof() )override { if ( traits::eq_int_type( c, traits::eof() ) ) { return m_sbuf->sputc( static_cast< char_type >( c ) ); } if ( m_set ) { std::fill_n( std::ostreambuf_iterator< char_type >( m_sbuf ), m_count, fill_char ); m_set = false; } if ( traits::eq_int_type( m_sbuf->sputc( static_cast< char_type >( c ) ), traits::eof() ) ) { return traits::eof(); } if ( traits::eq_int_type( c, traits::to_char_type( '\n' ) ) ) { m_set = true; } return traits::not_eof( c ); } private: std::basic_streambuf< char_type, traits > * m_sbuf; long m_count{ 0 }; bool m_set{ false }; }; template< typename char_type, typename traits = std::char_traits< char_type > > class BasicIndentBufferManager { private: using bos = std::ios_base; using bsb = std::basic_streambuf< char_type, traits >; using table_type = std::map< bos *, bsb * >; using value_type = typename table_type::value_type; using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; using lock_type = std::unique_lock< std::mutex >; BasicIndentBufferManager() { ++sm_instances; } BasicIndentBufferManager( BasicIndentBufferManager< char_type, traits > & obj ) = delete; public: ~BasicIndentBufferManager()noexcept { try { --sm_instances; lock_type lock{ m_mutex }; for ( iterator it = m_list.begin(); it != m_list.end(); ++it ) { delete it->second; } } catch ( ... ) { // What to do here ? } } bool insert( bos & o_s, bsb * b_s ) { lock_type lock{ m_mutex }; return m_list.emplace( &o_s, b_s ).second; } size_t size() { lock_type lock{ m_mutex }; return m_list.size(); } bsb * getBuffer( std::ios_base & io_s ) { lock_type lock{ m_mutex }; const_iterator cb_iter( m_list.find( &io_s ) ); bsb * result{}; if ( cb_iter != m_list.end() ) { result = cb_iter->second; } return result; } bool erase( std::ios_base & io_s ) { delete getBuffer( io_s ); lock_type lock{ m_mutex }; return ( m_list.erase( &io_s ) == 1u ); } static size_t instances() { return size_t( sm_instances ); } static BasicIndentBufferManager< char_type, traits > * instance() { static BasicIndentBufferManager< char_type, traits > ibm; return &ibm; } private: static std::atomic_int sm_instances; table_type m_list; std::mutex m_mutex; }; template< typename char_type, typename traits > std::atomic_int BasicIndentBufferManager< char_type, traits >::sm_instances = 0; inline void addIndent( std::ios_base & ios, long val ); inline long & indentValue( std::ios_base & ios ) { static int indentIndex = std::ios_base::xalloc(); return ios.iword( indentIndex ); } inline long getIndent( std::ios_base & ios ) { return indentValue( ios ); } inline void addIndent( std::ios_base & ios, long val ) { indentValue( ios ) += val; } template< typename CharType, typename BufferType = BasicIndentBuffer< CharType >, typename BufferManagerType = BasicIndentBufferManager< CharType > > inline BufferType * installIndentBuffer( std::basic_ostream< CharType > & stream ) { BufferType * sbuf( new BufferType( stream.rdbuf() ) ); BufferManagerType::instance()->insert( stream, sbuf ); stream.rdbuf( sbuf ); return sbuf; } template< typename CharType > inline void callback( std::ios_base::event ev, std::ios_base & ios, int ) { if ( BasicIndentBufferManager< CharType >::instances() && ev == std::ios_base::erase_event ) { BasicIndentBufferManager< CharType >::instance()->erase( ios ); } } struct Indent { explicit Indent( long i ) : indent( i ) { } long indent; private: template< typename CharType > friend std::basic_ostream< CharType > & operator<<( std::basic_ostream< CharType > & stream, Indent const & ind ) { auto sbuf = dynamic_cast< BasicIndentBuffer< CharType > * >( stream.rdbuf() ); if ( !sbuf ) { sbuf = installIndentBuffer( stream ); stream.register_callback( callback< CharType >, 0 ); } addIndent( stream, ind.indent ); sbuf->indent( ind.indent ); return stream; } }; static std::string_view constexpr imgColour{ "#8b008b" }; static std::string_view constexpr bufColour{ "#458b00" }; static std::string_view constexpr extColour{ "#ff7f00" }; static std::string_view constexpr passColour{ "#00007f" }; struct FramePassGroupStreams { using FramePassGroupStreamsPtr = std::unique_ptr< FramePassGroupStreams >; explicit FramePassGroupStreams( Config const & config , FramePassGroupStreams * parent = nullptr , FramePassGroup const * group = nullptr ) : m_config{ config } , m_parent{ parent } , m_group{ group } { } std::pair< FramePassGroupStreams *, bool > emplace( FramePassGroup const * group ) { auto streams = find( group ); if ( streams ) return { streams, false }; assert( group != nullptr ); if ( group->getParent() == m_group ) { m_children.emplace_back( std::make_unique< FramePassGroupStreams >( m_config, this, group ) ); streams = m_children.back().get(); } else { streams = emplace( group->getParent() ).first; streams = streams->emplace( group ).first; } return { streams, true }; } FramePassGroupStreams * find( FramePassGroup const * group ) { if ( m_group == group ) return this; FramePassGroupStreams * result{}; auto it = std::find_if( m_children.begin(), m_children.end() , [group, &result]( FramePassGroupStreamsPtr const & lookup ) { auto ret = lookup->find( group ); if ( ret ) result = ret; return ret != nullptr; } ); return it == m_children.end() ? nullptr : result; } void doWriteSplitted( DisplayResult & streams )const { if ( m_group ) { auto & stream = streams.try_emplace( m_group->getName() ).first->second; stream << "digraph \"" << m_group->getName() << "\" {\n"; stream << m_stream.str(); stream << "}\n"; } for ( auto & group : m_children ) group->write( streams ); } void doWriteUnsplittedWithGroups( DisplayResult & streams , std::stringstream const & global )const { auto & stream = streams.try_emplace( m_group ? m_group->getName() : std::string{} ).first->second; if ( m_group ) { stream << "subgraph cluster_" << m_group->getId() << " {\n"; stream << " label=\"" << m_group->getName() << "\";\n"; if ( m_config.withColours ) { static std::vector< std::string_view > colours { { "#FFFFFF" }, { "#EEEEEE" }, { "#DDDDDD" }, { "#CCCCCC" }, { "#BBBBBB" }, { "#AAAAAA" }, { "#999999" }, { "#888888" }, }; stream << " style=filled;\n"; stream << " fillcolor=\"" << colours[std::min( size_t( getLevel() ), colours.size() - 1u )] << "\";\n"; stream << " color=black;\n"; } } else { stream << "digraph {\n"; } stream << Indent{ 2 }; for ( auto & group : m_children ) { group->write( streams ); stream << streams.find( group->getName() )->second.str(); } auto & stream2 = streams.try_emplace( m_group ? m_group->getName() : std::string{} ).first->second; stream2 << Indent{ -2 }; stream2 << m_stream.str(); if ( !m_group ) stream2 << global.str(); stream2 << "}\n"; } void doWriteUnsplitted( DisplayResult & streams , std::stringstream const & global )const { auto & stream = streams.try_emplace( m_group ? m_group->getName() : std::string{} ).first->second; if ( !m_group ) stream << "digraph {\n"; for ( auto & group : m_children ) { group->write( streams ); stream << streams.find( group->getName() )->second.str(); } auto & stream2 = streams.try_emplace( m_group ? m_group->getName() : std::string{} ).first->second; stream2 << m_stream.str(); if ( !m_group ) { stream2 << global.str(); stream2 << "}\n"; } } void write( DisplayResult & streams , std::stringstream const & global = std::stringstream{} )const { if ( m_config.splitGroups ) doWriteSplitted( streams ); else if ( m_config.withGroups ) doWriteUnsplittedWithGroups( streams, global ); else doWriteUnsplitted( streams, global ); } std::string const & getName()const { static std::string const dummy; return m_group ? m_group->getName() : dummy; } std::set< std::string, std::less<> > & getNodes() { return m_nodes; } FramePassGroupStreams * getParent()const { return m_parent; } uint32_t getLevel()const { if ( m_parent ) return ( m_parent->m_parent ? 1u + m_parent->getLevel() : m_parent->getLevel() ); return 0u; } std::stringstream & getStream()const { return m_stream; } private: Config const & m_config; mutable std::stringstream m_stream; FramePassGroupStreams * m_parent; std::vector< FramePassGroupStreamsPtr > m_children; FramePassGroup const * m_group; std::set< std::string, std::less<> > m_nodes; }; static void displayNode( std::ostream & stream , std::string const & name , std::string const & shape , std::string_view colour , std::set< std::string, std::less<> > & nodes , Config const & config ) { if ( nodes.insert( name ).second ) { stream << Indent{ 2 }; stream << "\"" << name << "\" [ shape=" << shape; if ( config.withColours ) { stream << " style=filled"; stream << " fillcolor=white"; stream << " color=\"" << colour << "\""; } stream << " ];\n"; stream << Indent{ -2 }; } } static FramePassGroupStreams & displayGroupNode( FramePassGroup const * group , FramePassGroupStreams & groups , Config const & ) { return *groups.emplace( group ).first; } static FramePassGroupStreams & displayPassNode( uint32_t id , std::string const & name , FramePassGroup const * group , std::string_view colour , FramePassGroupStreams & groups , Config const & config ) { auto & groupStream = displayGroupNode( group, groups, config ); auto & stream = groupStream.getStream(); if ( groupStream.getNodes().insert( name ).second ) { stream << Indent{ 2 }; stream << "\"" << name << "\" [ shape=ellipse"; if ( config.withIds ) { stream << " id=\"" << id << "\""; } if ( config.withColours ) { stream << " style=filled"; stream << " fillcolor=white"; stream << " color=\"" << colour << "\""; } stream << " ];\n"; stream << Indent{ -2 }; } return groupStream; } static void displayEdge( std::ostream & stream , std::string const & from , std::string const & to , std::string const & label , std::string_view colour , Config const & config ) { stream << Indent{ 2 }; stream << "\"" << from << "\" -> \"" << to << "\" [ label=\"" << label << "\""; if ( config.withColours ) { stream << " color = \"" << colour << "\" fontcolor=\"" << colour << "\""; } stream << " ];\n"; stream << Indent{ -2 }; } static void displayAttachPass( Attachment const & attach , FramePassGroupStreams & groups , Config const & config ) { uint32_t nodeId = 0; std::string node = "ExternalSource"; FramePassGroup const * group = nullptr; std::string_view colour = extColour; if ( attach.pass ) { nodeId = attach.pass->getId(); node = attach.pass->getGroupName(); group = &attach.pass->getGroup(); colour = passColour; } displayPassNode( nodeId, node, group, colour, groups, config ); } static std::string const & getAttachName( crg::BufferViewId const & data ) { return data.data->name; } static std::string const & getAttachName( crg::ImageViewId const & data ) { return data.data->name; } static bool isIn( FramePassGroupStreams const * inner , FramePassGroupStreams const * outer ) { return outer == inner || ( outer && isIn( inner, outer->getParent() ) ); } static FramePassGroupStreams * getCommonGroup( FramePassGroupStreams * lhs , FramePassGroupStreams const * rhs ) { auto current = lhs; while ( current && !isIn( current, rhs ) ) { current = current->getParent(); } return current; } template< typename TransitionT > static void displayTransitionEdge( std::ostream & stream , std::string_view colour , TransitionT const & transition , FramePassGroupStreams & groups , Config const & config ) { auto srcName = transition.outputAttach.name; auto dstName = transition.inputAttach.name; std::string srcNode = "ExternalSource"; std::string dstNode = "ExternalDestination"; auto srcStream = &groups; auto dstStream = &groups; auto curStreams = &groups; if ( transition.outputAttach.pass ) { srcNode = transition.outputAttach.pass->getGroupName(); srcStream = groups.find( &transition.outputAttach.pass->getGroup() ); } if ( transition.inputAttach.pass ) { dstNode = transition.inputAttach.pass->getGroupName(); dstStream = groups.find( &transition.inputAttach.pass->getGroup() ); } std::string name{ "Transition to\\n" + dstName }; auto curstream = &stream; if ( srcStream != &groups && dstStream != &groups ) { if ( srcStream == dstStream ) { curStreams = srcStream; curstream = &srcStream->getStream(); } else if ( auto groupStreams = getCommonGroup( srcStream, dstStream ) ) { curStreams = groupStreams; curstream = &groupStreams->getStream(); } } displayNode( *curstream, name, "box", colour, curStreams->getNodes(), config ); displayEdge( *curstream, srcNode, name, getAttachName( transition.data ), colour, config ); displayEdge( *curstream, name, dstNode, getAttachName( transition.data ), colour, config ); } class DotTransitionsVisitor : public GraphVisitor { public: static void submit( DisplayResult & streams , ConstGraphAdjacentNode node , Config const & config ) { std::set< ConstGraphAdjacentNode > visited; std::stringstream trstream; FramePassGroupStreams groups{ config }; submit( streams, node, config, groups, trstream, visited ); groups.write( streams, trstream ); } private: static void submit( DisplayResult & streams , ConstGraphAdjacentNode node , Config const & config , FramePassGroupStreams & groups , std::stringstream & trstream , std::set< ConstGraphAdjacentNode > & visited ) { DotTransitionsVisitor vis{ streams, groups, trstream, config, visited }; node->accept( &vis ); } DotTransitionsVisitor( DisplayResult & streams , FramePassGroupStreams & groups , std::stringstream & trstream , Config const & config , std::set< ConstGraphAdjacentNode > & visited ) : m_streams{ streams } , m_groups{ groups } , m_trstream{ trstream } , m_config{ config } , m_visited{ visited } { } void submit( ConstGraphAdjacentNode node ) { submit( m_streams , node , m_config , m_groups , m_trstream , m_visited ); } void visitRootNode( RootNode const * node )override { for ( auto & pred : node->getPredecessors() ) { submit( m_streams , pred , m_config , m_groups , m_trstream , m_visited ); } } void visitFramePassNode( FramePassNode const * node )override { for ( auto & transition : node->getImageTransitions() ) { displayAttachPass( transition.outputAttach, m_groups, m_config ); displayAttachPass( transition.inputAttach, m_groups, m_config ); } for ( auto & transition : node->getBufferTransitions() ) { displayAttachPass( transition.outputAttach, m_groups, m_config ); displayAttachPass( transition.inputAttach, m_groups, m_config ); } for ( auto & transition : node->getImageTransitions() ) { displayTransitionEdge( m_trstream, imgColour, transition, m_groups, m_config ); } for ( auto & transition : node->getBufferTransitions() ) { displayTransitionEdge( m_trstream, bufColour, transition, m_groups, m_config ); } m_visited.insert( node ); for ( auto & pred : node->getPredecessors() ) { if ( m_visited.end() == m_visited.find( pred ) ) { submit( pred ); } } } private: DisplayResult & m_streams; FramePassGroupStreams & m_groups; std::stringstream & m_trstream; Config const & m_config; std::set< GraphNode const * > & m_visited; }; static std::string applyRemove( std::string const & text, Config const & config ) { if ( config.toRemove.empty() ) return text; auto result = text; size_t startPos = 0u; while ( ( startPos = result.find( config.toRemove, startPos ) ) != std::string::npos ) result.replace( startPos, config.toRemove.length(), "" ); return result; } } DisplayResult displayTransitions( RunnableGraph const & value , Config const & config ) { DisplayResult result; dotexp::DotTransitionsVisitor::submit( result, value.getNodeGraph(), config ); return result; } void displayTransitions( std::ostream & stream , RunnableGraph const & value , Config const & config ) { auto result = displayTransitions( value, config ); if ( config.splitGroups ) { for ( auto const & [name, strm] : result ) { if ( !name.empty() ) { stream << dotexp::applyRemove( strm.str(), config ); } } } else { auto it = result.find( std::string{} ); stream << dotexp::applyRemove( it->second.str(), config ); } } } ================================================ FILE: source/RenderGraph/FrameGraph.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/FrameGraph.hpp" #include "RenderGraph/Exception.hpp" #include "RenderGraph/FramePass.hpp" #include "RenderGraph/FramePassGroup.hpp" #include "RenderGraph/Hash.hpp" #include "RenderGraph/Log.hpp" #include "RenderGraph/ResourceHandler.hpp" #include "RenderGraph/RunnableGraph.hpp" #include "GraphBuilder.hpp" #include namespace crg { namespace fgph { static void mergeViewData( ImageViewId const & view , bool mergeMipLevels , bool mergeArrayLayers , ImageViewData & data ) { if ( data.image.id == 0 ) { data.image = view.data->image; data.name = data.image.data->name; data.info.flags = view.data->info.flags; data.info.format = view.data->info.format; data.info.viewType = view.data->info.viewType; data.info.subresourceRange = getSubresourceRange( view ); } else { assert( data.image == view.data->image ); if ( mergeMipLevels ) { auto maxLevel = std::max( data.info.subresourceRange.levelCount + data.info.subresourceRange.baseMipLevel , getSubresourceRange( view ).levelCount + getSubresourceRange( view ).baseMipLevel ); data.info.subresourceRange.baseMipLevel = std::min( getSubresourceRange( view ).baseMipLevel , data.info.subresourceRange.baseMipLevel ); data.info.subresourceRange.levelCount = maxLevel - data.info.subresourceRange.baseMipLevel; } else { data.info.subresourceRange.baseMipLevel = std::min( getSubresourceRange( view ).baseMipLevel , data.info.subresourceRange.baseMipLevel ); data.info.subresourceRange.levelCount = 1u; } if ( mergeArrayLayers ) { auto maxLayer = std::max( data.info.subresourceRange.layerCount + data.info.subresourceRange.baseArrayLayer , getSubresourceRange( view ).layerCount + getSubresourceRange( view ).baseArrayLayer ); data.info.subresourceRange.baseArrayLayer = std::min( getSubresourceRange( view ).baseArrayLayer , data.info.subresourceRange.baseArrayLayer ); data.info.subresourceRange.layerCount = maxLayer - data.info.subresourceRange.baseArrayLayer; } else { data.info.subresourceRange.baseArrayLayer = std::min( getSubresourceRange( view ).baseArrayLayer , data.info.subresourceRange.baseArrayLayer ); data.info.subresourceRange.layerCount = 1u; } } data.source.push_back( view ); } static void mergeViewData( BufferViewId const & view , BufferViewData & data ) { if ( data.buffer.id == 0 ) { data.buffer = view.data->buffer; data.name = data.buffer.data->name; data.info.format = view.data->info.format; data.info.subresourceRange = getSubresourceRange( view ); } else { assert( data.buffer == view.data->buffer ); auto maxUpperBound = std::max( data.info.subresourceRange.offset + data.info.subresourceRange.size , getSubresourceRange( view ).offset + getSubresourceRange( view ).size ); auto minLowerBound = std::min( data.info.subresourceRange.offset , getSubresourceRange( view ).offset ); data.info.subresourceRange.offset = minLowerBound; data.info.subresourceRange.size = maxUpperBound - minLowerBound; } data.source.push_back( view ); } static size_t makeHash( AttachmentArray const & attachments , bool mergeMipLevels , bool mergeArrayLayers ) { auto result = std::hash< bool >{}( mergeMipLevels ); result = hashCombine( result, mergeArrayLayers ); for ( auto attach : attachments ) result = hashCombine( result, attach ); return result; } static AttachmentPtr mergeAttachments( FrameGraph & graph , AttachmentArray const & attachments , uint32_t passCount , bool mergeMipLevels , bool mergeArrayLayers ) { AttachmentPtr result; for ( uint32_t passIndex = 0u; passIndex < passCount; ++passIndex ) { ImageViewIdArray views; for ( auto attach : attachments ) { views.push_back( attach->view( passIndex ) ); if ( !result ) { result = std::make_unique< Attachment >( views.back(), *attach ); result->imageAttach.views.clear(); result->pass = nullptr; } result->source.emplace_back( attach, attach->pass, attach->imageAttach ); } result->imageAttach.views.push_back( graph.mergeViews( views, mergeMipLevels, mergeArrayLayers ) ); } return result; } static AttachmentPtr mergeAttachments( FrameGraph & graph , AttachmentArray const & attachments , uint32_t passCount ) { AttachmentPtr result; for ( uint32_t passIndex = 0u; passIndex < passCount; ++passIndex ) { BufferViewIdArray views; for ( auto attach : attachments ) { views.push_back( attach->buffer( passIndex ) ); if ( !result ) { result = std::make_unique< Attachment >( views.back(), *attach ); result->bufferAttach.buffers.clear(); result->pass = nullptr; } result->source.emplace_back( attach, attach->pass, attach->bufferAttach ); } result->bufferAttach.buffers.push_back( graph.mergeViews( views ) ); } return result; } } FrameGraph::FrameGraph( ResourceHandler & handler , std::string name ) : m_handler{ handler } , m_name{ std::move( name ) } , m_defaultGroup{ std::make_unique< FramePassGroup >( *this, 0u, m_name, FramePassGroup::Token{} ) } , m_finalState{ handler } { } FramePass & FrameGraph::createPass( std::string const & name , RunnablePassCreator runnableCreator ) { return m_defaultGroup->createPass( name, std::move( runnableCreator ) ); } FramePassGroup & FrameGraph::createPassGroup( std::string const & groupName ) { return m_defaultGroup->createPassGroup( groupName ); } BufferId FrameGraph::createBuffer( BufferData const & img ) { auto result = m_handler.createBufferId( img ); m_buffers.insert( result ); return result; } BufferViewId FrameGraph::createView( BufferViewData const & view ) { auto result = m_handler.createViewId( view ); m_bufferViews.insert( result ); return result; } ImageId FrameGraph::createImage( ImageData const & img ) { auto result = m_handler.createImageId( img ); m_images.insert( result ); return result; } ImageViewId FrameGraph::createView( ImageViewData const & view ) { auto result = m_handler.createViewId( view ); m_imageViews.insert( result ); return result; } ImageViewId FrameGraph::mergeViews( ImageViewIdArray const & views , bool mergeMipLevels , bool mergeArrayLayers ) { ImageViewData data; for ( auto & view : views ) fgph::mergeViewData( view, mergeMipLevels, mergeArrayLayers, data ); if ( data.info.subresourceRange.layerCount > 1u ) { switch ( data.info.viewType ) { case ImageViewType::e1D: data.info.viewType = ImageViewType::e1DArray; break; case ImageViewType::e2D: if ( checkFlag( data.image.data->info.flags, ImageCreateFlags::eCubeCompatible ) && ( data.info.subresourceRange.layerCount % 6u ) == 0u && data.info.subresourceRange.baseArrayLayer == 0u ) data.info.viewType = ( data.info.subresourceRange.layerCount > 6u ) ? ImageViewType::eCubeArray : ImageViewType::eCube; else data.info.viewType = ImageViewType::e2DArray; break; case ImageViewType::eCube: if ( data.info.subresourceRange.layerCount > 6u ) data.info.viewType = ImageViewType::eCubeArray; break; default: break; } } return createView( data ); } BufferViewId FrameGraph::mergeViews( BufferViewIdArray const & views ) { BufferViewData data; for ( auto & view : views ) fgph::mergeViewData( view, data ); return createView( data ); } Attachment const * FrameGraph::mergeAttachments( AttachmentArray const & attachments , bool mergeMipLevels , bool mergeArrayLayers ) { if ( attachments.empty() ) { Logger::logWarning( "No attachments to merge" ); return nullptr; } if ( attachments.size() == 1 ) { Logger::logDebug( "Single attachment, nothing to merge" ); return attachments.front(); } auto allImages = std::all_of( attachments.begin(), attachments.end() , []( Attachment const * attach ) { return attach->isImage(); } ); if ( auto allBuffers = std::all_of( attachments.begin(), attachments.end() , []( Attachment const * attach ) { return attach->isBuffer(); } ); !allImages && !allBuffers ) { Logger::logWarning( "Can only merge attachments of the same type" ); CRG_Exception( "Can only merge attachments of the same type" ); } auto passCount = allImages ? attachments.front()->getViewCount() : attachments.front()->getBufferCount(); if ( passCount == 0 ) { Logger::logWarning( "Can't merge empty attachments" ); CRG_Exception( "Can't merge empty attachments" ); } if ( allImages ) { if ( !std::all_of( attachments.begin(), attachments.end() , [passCount]( Attachment const * attach ) { return attach->getViewCount() == passCount; } ) ) { Logger::logWarning( "Can only merge attachments with the same pass count" ); CRG_Exception( "Can only merge attachments with the same pass count" ); } } else { if ( !std::all_of( attachments.begin(), attachments.end() , [passCount]( Attachment const * attach ) { return attach->getBufferCount() == passCount; } ) ) { Logger::logWarning( "Can only merge attachments with the same pass count" ); CRG_Exception( "Can only merge attachments with the same pass count" ); } } size_t hash = allImages ? fgph::makeHash( attachments, mergeMipLevels, mergeArrayLayers ) : fgph::makeHash( attachments, false, false ); auto [it, inserted] = m_mergedAttachments.try_emplace( hash, nullptr ); if ( inserted ) { if ( allImages ) { it->second = fgph::mergeAttachments( *this, attachments, passCount, mergeMipLevels, mergeArrayLayers ); } else { it->second = fgph::mergeAttachments( *this, attachments, passCount ); } it->second->initSources(); } return it->second.get(); } RunnableGraphPtr FrameGraph::compile( GraphContext & context ) { FramePassArray passes; m_defaultGroup->listPasses( passes ); if ( passes.empty() ) { Logger::logWarning( "No FramePass registered." ); CRG_Exception( "No FramePass registered." ); } auto endPoints = builder::findEndPoints( passes ); RootNode root{ *this }; GraphNodePtrArray nodes; builder::buildGraph( endPoints, root, nodes, context.separateDepthStencilLayouts ); return std::make_unique< RunnableGraph >( *this , std::move( nodes ) , std::move( root ) , context ); } LayoutState FrameGraph::getFinalLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range )const { return m_finalState.getLayoutState( image, viewType, range ); } LayoutState FrameGraph::getFinalLayoutState( ImageViewId view , uint32_t passIndex )const { if ( view.data->source.empty() ) { return getFinalLayoutState( view.data->image , view.data->info.viewType , getSubresourceRange( view ) ); } return getFinalLayoutState( view.data->source[passIndex], 0u ); } AccessState const & FrameGraph::getFinalAccessState( BufferId buffer , BufferSubresourceRange const & range )const { return m_finalState.getAccessState( buffer, range ); } AccessState const & FrameGraph::getFinalAccessState( BufferViewId view , uint32_t passIndex )const { if ( view.data->source.empty() ) { return getFinalAccessState( view.data->buffer , getSubresourceRange( view ) ); } return getFinalAccessState( view.data->source[passIndex], 0u ); } void FrameGraph::addInput( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range , LayoutState const & outputLayout ) { m_inputs.setLayoutState( image , viewType , range , outputLayout ); } void FrameGraph::addInput( ImageViewId view , LayoutState const & outputLayout ) { addInput( view.data->image , view.data->info.viewType , getSubresourceRange( view ) , outputLayout ); } LayoutState FrameGraph::getInputLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range )const { return m_inputs.getLayoutState( image , viewType , range ); } LayoutState FrameGraph::getInputLayoutState( ImageViewId view )const { return getInputLayoutState( view.data->image , view.data->info.viewType , getSubresourceRange( view ) ); } void FrameGraph::addOutput( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range , LayoutState const & outputLayout ) { m_outputs.setLayoutState( image , viewType , range , outputLayout ); } void FrameGraph::addOutput( ImageViewId view , LayoutState const & outputLayout ) { addOutput( view.data->image , view.data->info.viewType , getSubresourceRange( view ) , outputLayout ); } LayoutState FrameGraph::getOutputLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range )const { return m_outputs.getLayoutState( image , viewType , range ); } LayoutState FrameGraph::getOutputLayoutState( ImageViewId view )const { return getOutputLayoutState( view.data->image , view.data->info.viewType , getSubresourceRange( view ) ); } LayerLayoutStatesMap const & FrameGraph::getOutputLayoutStates()const { return m_outputs.images; } void FrameGraph::registerFinalState( RecordContext const & context ) { m_finalState = context; } } ================================================ FILE: source/RenderGraph/FrameGraph.natvis ================================================ {name,sb} fmt={info.size} name info.flags info.size info.usage {name,sb} fmt={info.format} offset={info.subresourceRange.offset} size={info.subresourceRange.size} name buffer info.format info.subresourceRange.offset info.subresourceRange.size {name,sb} fmt={info.format} mips={info.mipLevels} name info.flags info.imageType info.format info.extent info.mipLevels info.arrayLayers info.samples info.tiling info.usage {name,sb} fmt={info.format} mipLevel={info.subresourceRange.baseMipLevel} mipCount={info.subresourceRange.levelCount} name image info.flags info.viewType info.format info.subresourceRange.baseArrayLayer info.subresourceRange.layerCount info.subresourceRange.baseMipLevel info.subresourceRange.levelCount {id} {data->name,sb} id *data {buffers[0]} Uniform {buffers[0]} Storage {buffers[0]} Transfer {buffers[0]} Transition {buffers[0]} buffers wantedAccess {views[0]} Sampled {views[0]}" Storage {views[0]}" Transfer {views[0]}" Transition {views[0]}" Target {views[0]} views loadOp storeOp stencilLoadOp stencilStoreOp blendState clearValue wantedLayout Image {imageAttach} ({*pass})" Buffer {bufferAttach} ({*pass})" Image {imageAttach}" Buffer {bufferAttach}" Image In {imageAttach} ({*pass})" Image Out {imageAttach} ({*pass})" Image InOut {imageAttach} ({*pass})" Buffer In {bufferAttach} ({*pass})" Buffer Out {bufferAttach} ({*pass})" Buffer InOut {bufferAttach} ({*pass})" Image In {imageAttach}" Image Out {imageAttach}" Image InOut {imageAttach}" Buffer In {bufferAttach}" Buffer Out {bufferAttach}" Buffer InOut {bufferAttach}" name imageAttach bufferAttach *pass source {m_group}/{m_id} {m_name,sb} m_group m_id m_name m_uniforms m_sampled m_inputs m_inouts m_outputs m_targets m_ownAttaches {m_name,sb} {*m_parent}/{m_name,sb} m_id m_name m_passes m_groups m_parent m_graph m_inputs m_outputs {kind} {name} name kind prev attachsToPrev {kind} {*pass} *pass prev attachsToPrev {kind} {name} name prev {*attach}, passes={passes} attach passes src={*srcPass}, dst={*dstPass}, in={dstInputs}, out={srcOutputs} *srcPass *dstPass srcOutputs dstInputs {data.data->name} : {outputAttach.name} -> {inputAttach.name} data outputAttach inputAttach {data.name} : {outputAttach.name} -> {inputAttach.name} data outputAttach inputAttach {m_pass} m_pass m_context m_graph m_callbacks m_ruConfig m_timer m_passContexts {view} {*pass} pass view action ================================================ FILE: source/RenderGraph/FrameGraphPrerequisites.cpp ================================================ #include "RenderGraph/FrameGraphPrerequisites.hpp" #include "RenderGraph/BufferData.hpp" #include "RenderGraph/BufferViewData.hpp" #include "RenderGraph/ImageData.hpp" #include "RenderGraph/ImageViewData.hpp" #include namespace crg { namespace fgph { static bool match( ImageSubresourceRange const & lhsRange , ImageSubresourceRange const & rhsRange )noexcept { return ( ( lhsRange.aspectMask & rhsRange.aspectMask ) != ImageAspectFlags::eNone ) && lhsRange.baseArrayLayer == rhsRange.baseArrayLayer && lhsRange.layerCount == rhsRange.layerCount && lhsRange.baseMipLevel == rhsRange.baseMipLevel && lhsRange.levelCount == rhsRange.levelCount; } static bool match( ImageId const & image , ImageViewType lhsType , ImageViewType rhsType , ImageSubresourceRange const & lhsRange , ImageSubresourceRange const & rhsRange )noexcept { return match( getVirtualRange( image, lhsType, lhsRange ) , getVirtualRange( image, rhsType, rhsRange ) ); } static bool match( ImageId const & image , ImageViewCreateInfo const & lhsInfo , ImageViewCreateInfo const & rhsInfo )noexcept { return lhsInfo.flags == rhsInfo.flags && lhsInfo.format == rhsInfo.format && match( image , lhsInfo.viewType, rhsInfo.viewType , lhsInfo.subresourceRange, rhsInfo.subresourceRange ); } static bool match( ImageViewData const & lhs, ImageViewData const & rhs )noexcept { return lhs.image.id == rhs.image.id && match( lhs.image, lhs.info, rhs.info ); } } //********************************************************************************************* std::string_view getName( PixelFormat format ) { switch ( format ) { case PixelFormat::eR4G4_UNORM: return "rg8"; case PixelFormat::eR4G4B4A4_UNORM: return "rgba16"; case PixelFormat::eB4G4R4A4_UNORM: return "rgba16s"; case PixelFormat::eR5G6B5_UNORM: return "rgb565"; case PixelFormat::eB5G6R5_UNORM: return "bgr565"; case PixelFormat::eR5G5B5A1_UNORM: return "rgba5551"; case PixelFormat::eB5G5R5A1_UNORM: return "bgra5551"; case PixelFormat::eA1R5G5B5_UNORM: return "argb1555"; case PixelFormat::eR8_UNORM: return "r8"; case PixelFormat::eR8_SNORM: return "r8s"; case PixelFormat::eR8_USCALED: return "r8us"; case PixelFormat::eR8_SSCALED: return "r8ss"; case PixelFormat::eR8_UINT: return "r8ui"; case PixelFormat::eR8_SINT: return "r8si"; case PixelFormat::eR8_SRGB: return "r8srgb"; case PixelFormat::eR8G8_UNORM: return "rg16"; case PixelFormat::eR8G8_SNORM: return "rg16s"; case PixelFormat::eR8G8_USCALED: return "rg16us"; case PixelFormat::eR8G8_SSCALED: return "rg16ss"; case PixelFormat::eR8G8_UINT: return "rg16ui"; case PixelFormat::eR8G8_SINT: return "rg16si"; case PixelFormat::eR8G8_SRGB: return "rg16srgb"; case PixelFormat::eR8G8B8_UNORM: return "rgb24"; case PixelFormat::eR8G8B8_SNORM: return "rgb24s"; case PixelFormat::eR8G8B8_USCALED: return "rgb24us"; case PixelFormat::eR8G8B8_SSCALED: return "rgb24ss"; case PixelFormat::eR8G8B8_UINT: return "rgb24ui"; case PixelFormat::eR8G8B8_SINT: return "rgb24si"; case PixelFormat::eR8G8B8_SRGB: return "rgb24srgb"; case PixelFormat::eB8G8R8_UNORM: return "bgr24"; case PixelFormat::eB8G8R8_SNORM: return "bgr24s"; case PixelFormat::eB8G8R8_USCALED: return "bgr24us"; case PixelFormat::eB8G8R8_SSCALED: return "bgr24ss"; case PixelFormat::eB8G8R8_UINT: return "bgr24ui"; case PixelFormat::eB8G8R8_SINT: return "bgr24si"; case PixelFormat::eB8G8R8_SRGB: return "bgr24srgb"; case PixelFormat::eR8G8B8A8_UNORM: return "rgba32"; case PixelFormat::eR8G8B8A8_SNORM: return "rgba32s"; case PixelFormat::eR8G8B8A8_USCALED: return "rgba32us"; case PixelFormat::eR8G8B8A8_SSCALED: return "rgba32ss"; case PixelFormat::eR8G8B8A8_UINT: return "rgba32ui"; case PixelFormat::eR8G8B8A8_SINT: return "rgba32si"; case PixelFormat::eR8G8B8A8_SRGB: return "rgba32srgb"; case PixelFormat::eB8G8R8A8_UNORM: return "bgra32"; case PixelFormat::eB8G8R8A8_SNORM: return "bgra32s"; case PixelFormat::eB8G8R8A8_USCALED: return "bgra32us"; case PixelFormat::eB8G8R8A8_SSCALED: return "bgra32ss"; case PixelFormat::eB8G8R8A8_UINT: return "bgra32ui"; case PixelFormat::eB8G8R8A8_SINT: return "bgra32si"; case PixelFormat::eB8G8R8A8_SRGB: return "bgra32srgb"; case PixelFormat::eA8B8G8R8_UNORM: return "abgr32"; case PixelFormat::eA8B8G8R8_SNORM: return "abgr32s"; case PixelFormat::eA8B8G8R8_USCALED: return "abgr32us"; case PixelFormat::eA8B8G8R8_SSCALED: return "abgr32ss"; case PixelFormat::eA8B8G8R8_UINT: return "abgr32ui"; case PixelFormat::eA8B8G8R8_SINT: return "abgr32si"; case PixelFormat::eA8B8G8R8_SRGB: return "abgr32srgb"; case PixelFormat::eA2R10G10B10_UNORM: return "argb2101010"; case PixelFormat::eA2R10G10B10_SNORM: return "argb2101010s"; case PixelFormat::eA2R10G10B10_USCALED: return "argb2101010us"; case PixelFormat::eA2R10G10B10_SSCALED: return "argb2101010ss"; case PixelFormat::eA2R10G10B10_UINT: return "argb2101010ui"; case PixelFormat::eA2R10G10B10_SINT: return "argb2101010si"; case PixelFormat::eA2B10G10R10_UNORM: return "abgr2101010"; case PixelFormat::eA2B10G10R10_SNORM: return "abgr2101010s"; case PixelFormat::eA2B10G10R10_USCALED: return "abgr2101010us"; case PixelFormat::eA2B10G10R10_SSCALED: return "abgr2101010ss"; case PixelFormat::eA2B10G10R10_UINT: return "abgr2101010ui"; case PixelFormat::eA2B10G10R10_SINT: return "abgr2101010si"; case PixelFormat::eR16_UNORM: return "r16"; case PixelFormat::eR16_SNORM: return "rg16s"; case PixelFormat::eR16_USCALED: return "rg16us"; case PixelFormat::eR16_SSCALED: return "rg16ss"; case PixelFormat::eR16_UINT: return "rg16ui"; case PixelFormat::eR16_SINT: return "rg16si"; case PixelFormat::eR16_SFLOAT: return "rg16f"; case PixelFormat::eR16G16_UNORM: return "rg32"; case PixelFormat::eR16G16_SNORM: return "rg32s"; case PixelFormat::eR16G16_USCALED: return "rg32us"; case PixelFormat::eR16G16_SSCALED: return "rg32ss"; case PixelFormat::eR16G16_UINT: return "rg32ui"; case PixelFormat::eR16G16_SINT: return "rg32si"; case PixelFormat::eR16G16_SFLOAT: return "rg32f"; case PixelFormat::eR16G16B16_UNORM: return "rgb48"; case PixelFormat::eR16G16B16_SNORM: return "rgb48s"; case PixelFormat::eR16G16B16_USCALED: return "rgb48us"; case PixelFormat::eR16G16B16_SSCALED: return "rgb48ss"; case PixelFormat::eR16G16B16_UINT: return "rgb48ui"; case PixelFormat::eR16G16B16_SINT: return "rgb48si"; case PixelFormat::eR16G16B16_SFLOAT: return "rgb48f"; case PixelFormat::eR16G16B16A16_UNORM: return "rgba64"; case PixelFormat::eR16G16B16A16_SNORM: return "rgba64s"; case PixelFormat::eR16G16B16A16_USCALED: return "rgba64us"; case PixelFormat::eR16G16B16A16_SSCALED: return "rgba64ss"; case PixelFormat::eR16G16B16A16_UINT: return "rgba64ui"; case PixelFormat::eR16G16B16A16_SINT: return "rgba64si"; case PixelFormat::eR16G16B16A16_SFLOAT: return "rgba64f"; case PixelFormat::eR32_UINT: return "r32ui"; case PixelFormat::eR32_SINT: return "r32si"; case PixelFormat::eR32_SFLOAT: return "r32f"; case PixelFormat::eR32G32_UINT: return "rg64ui"; case PixelFormat::eR32G32_SINT: return "rg64si"; case PixelFormat::eR32G32_SFLOAT: return "rg64f"; case PixelFormat::eR32G32B32_UINT: return "rgb96ui"; case PixelFormat::eR32G32B32_SINT: return "rgb96si"; case PixelFormat::eR32G32B32_SFLOAT: return "rgb96f"; case PixelFormat::eR32G32B32A32_UINT: return "rgba128ui"; case PixelFormat::eR32G32B32A32_SINT: return "rgba128si"; case PixelFormat::eR32G32B32A32_SFLOAT: return "rgba128f"; case PixelFormat::eR64_UINT: return "r64ui"; case PixelFormat::eR64_SINT: return "r64si"; case PixelFormat::eR64_SFLOAT: return "r64f"; case PixelFormat::eR64G64_UINT: return "rg128ui"; case PixelFormat::eR64G64_SINT: return "rg128si"; case PixelFormat::eR64G64_SFLOAT: return "rg128f"; case PixelFormat::eR64G64B64_UINT: return "rgb192ui"; case PixelFormat::eR64G64B64_SINT: return "rgb192si"; case PixelFormat::eR64G64B64_SFLOAT: return "rgb192f"; case PixelFormat::eR64G64B64A64_UINT: return "rgba256ui"; case PixelFormat::eR64G64B64A64_SINT: return "rgba256si"; case PixelFormat::eR64G64B64A64_SFLOAT: return "rgba256f"; case PixelFormat::eB10G11R11_UFLOAT: return "bgr32f"; case PixelFormat::eE5B9G9R9_UFLOAT: return "ebgr32f"; case PixelFormat::eD16_UNORM: return "depth16"; case PixelFormat::eX8_D24_UNORM: return "depth24"; case PixelFormat::eD32_SFLOAT: return "depth32f"; case PixelFormat::eS8_UINT: return "stencil8"; case PixelFormat::eD16_UNORM_S8_UINT: return "depth16s8"; case PixelFormat::eD24_UNORM_S8_UINT: return "depth24s8"; case PixelFormat::eD32_SFLOAT_S8_UINT: return "depth32fs8"; case PixelFormat::eBC1_RGB_UNORM_BLOCK: return "bc1_rgb"; case PixelFormat::eBC1_RGB_SRGB_BLOCK: return "bc1_srgb"; case PixelFormat::eBC1_RGBA_UNORM_BLOCK: return "bc1_rgba"; case PixelFormat::eBC1_RGBA_SRGB_BLOCK: return "bc1_rgba_srgb"; case PixelFormat::eBC2_UNORM_BLOCK: return "bc2_rgba"; case PixelFormat::eBC2_SRGB_BLOCK: return "bc2_rgba_srgb"; case PixelFormat::eBC3_UNORM_BLOCK: return "bc3_rgba"; case PixelFormat::eBC3_SRGB_BLOCK: return "bc3_rgba_srgb"; case PixelFormat::eBC4_UNORM_BLOCK: return "bc4_r"; case PixelFormat::eBC4_SNORM_BLOCK: return "bc4_r_s"; case PixelFormat::eBC5_UNORM_BLOCK: return "bc5_rg"; case PixelFormat::eBC5_SNORM_BLOCK: return "bc5_rg_s"; case PixelFormat::eBC6H_UFLOAT_BLOCK: return "bc6h"; case PixelFormat::eBC6H_SFLOAT_BLOCK: return "bc6h_s"; case PixelFormat::eBC7_UNORM_BLOCK: return "bc7"; case PixelFormat::eBC7_SRGB_BLOCK: return "bc7_srgb"; case PixelFormat::eETC2_R8G8B8_UNORM_BLOCK: return "etc2_rgb"; case PixelFormat::eETC2_R8G8B8_SRGB_BLOCK: return "etc2_rgb_srgb"; case PixelFormat::eETC2_R8G8B8A1_UNORM_BLOCK: return "etc2_rgba1"; case PixelFormat::eETC2_R8G8B8A1_SRGB_BLOCK: return "etc2_rgba1_srgb"; case PixelFormat::eETC2_R8G8B8A8_UNORM_BLOCK: return "etc2_rgba"; case PixelFormat::eETC2_R8G8B8A8_SRGB_BLOCK: return "etc2_rgba_srgb"; case PixelFormat::eEAC_R11_UNORM_BLOCK: return "eac_r"; case PixelFormat::eEAC_R11_SNORM_BLOCK: return "eac_r_s"; case PixelFormat::eEAC_R11G11_UNORM_BLOCK: return "eac_rg"; case PixelFormat::eEAC_R11G11_SNORM_BLOCK: return "eac_rg_s"; case PixelFormat::eASTC_4x4_UNORM_BLOCK: return "astc_4x4"; case PixelFormat::eASTC_4x4_SRGB_BLOCK: return "astc_4x4_srgb"; case PixelFormat::eASTC_5x4_UNORM_BLOCK: return "astc_5x4"; case PixelFormat::eASTC_5x4_SRGB_BLOCK: return "astc_5x4_srgb"; case PixelFormat::eASTC_5x5_UNORM_BLOCK: return "astc_5x5"; case PixelFormat::eASTC_5x5_SRGB_BLOCK: return "astc_5x5_srgb"; case PixelFormat::eASTC_6x5_UNORM_BLOCK: return "astc_6x5"; case PixelFormat::eASTC_6x5_SRGB_BLOCK: return "astc_6x5_srgb"; case PixelFormat::eASTC_6x6_UNORM_BLOCK: return "astc_6x6"; case PixelFormat::eASTC_6x6_SRGB_BLOCK: return "astc_6x6_srgb"; case PixelFormat::eASTC_8x5_UNORM_BLOCK: return "astc_8x5"; case PixelFormat::eASTC_8x5_SRGB_BLOCK: return "astc_8x5_srgb"; case PixelFormat::eASTC_8x6_UNORM_BLOCK: return "astc_8x6"; case PixelFormat::eASTC_8x6_SRGB_BLOCK: return "astc_8x6_srgb"; case PixelFormat::eASTC_8x8_UNORM_BLOCK: return "astc_8x8"; case PixelFormat::eASTC_8x8_SRGB_BLOCK: return "astc_8x8_srgb"; case PixelFormat::eASTC_10x5_UNORM_BLOCK: return "astc_10x5"; case PixelFormat::eASTC_10x5_SRGB_BLOCK: return "astc_10x5_srgb"; case PixelFormat::eASTC_10x6_UNORM_BLOCK: return "astc_10x6"; case PixelFormat::eASTC_10x6_SRGB_BLOCK: return "astc_10x6_srgb"; case PixelFormat::eASTC_10x8_UNORM_BLOCK: return "astc_10x8"; case PixelFormat::eASTC_10x8_SRGB_BLOCK: return "astc_10x8_srgb"; case PixelFormat::eASTC_10x10_UNORM_BLOCK: return "astc_10x10"; case PixelFormat::eASTC_10x10_SRGB_BLOCK: return "astc_10x10_srgb"; case PixelFormat::eASTC_12x10_UNORM_BLOCK: return "astc_12x10"; case PixelFormat::eASTC_12x10_SRGB_BLOCK: return "astc_12x10_srgb"; case PixelFormat::eASTC_12x12_UNORM_BLOCK: return "astc_12x12"; case PixelFormat::eASTC_12x12_SRGB_BLOCK: return "astc_12x12_srgb"; default: return "undefined"; } } //********************************************************************************************* std::string_view getName( FilterMode v ) { if ( v == FilterMode::eLinear ) return "linear"; return "nearest"; } //********************************************************************************************* std::string_view getName( MipmapMode v ) { if ( v == MipmapMode::eLinear ) return "linear"; return "nearest"; } //********************************************************************************************* std::string_view getName( WrapMode v ) { switch ( v ) { case WrapMode::eMirroredRepeat: return "mirrored_repeat"; case WrapMode::eClampToEdge: return "clamp_to_edge"; case WrapMode::eClampToBorder: return "clamp_to_border"; case WrapMode::eMirrorClampToEdge: return "mirrored_clamp_to_edge"; default: return "repeat"; } } //********************************************************************************************* ImageCreateFlags getImageCreateFlags( ImageId const & image )noexcept { return image.data->info.flags; } ImageCreateFlags getImageCreateFlags( ImageViewId const & image )noexcept { return getImageCreateFlags( image.data->image ); } Extent3D const & getExtent( ImageId const & image )noexcept { return image.data->info.extent; } Extent3D const & getExtent( ImageViewId const & image )noexcept { return getExtent( image.data->image ); } DeviceSize getSize( BufferId const & buffer )noexcept { return buffer.data->info.size; } DeviceSize getSize( BufferViewId const & buffer )noexcept { return buffer.data->info.subresourceRange.size; } Extent3D getMipExtent( ImageViewId const & image )noexcept { auto result = getExtent( image.data->image ); result.width >>= getSubresourceRange( image ).baseMipLevel; result.height >>= getSubresourceRange( image ).baseMipLevel; result.depth >>= getSubresourceRange( image ).baseMipLevel; return result; } PixelFormat getFormat( ImageId const & image )noexcept { return image.data->info.format; } PixelFormat getFormat( ImageViewId const & image )noexcept { return image.data->info.format; } ImageType getImageType( ImageId const & image )noexcept { return image.data->info.imageType; } ImageType getImageType( ImageViewId const & image )noexcept { return getImageType( image.data->image ); } ImageViewType getImageViewType( ImageViewId const & image )noexcept { return image.data->info.viewType; } uint32_t getMipLevels( ImageId const & image )noexcept { return image.data->info.mipLevels; } uint32_t getMipLevels( ImageViewId const & image )noexcept { return getSubresourceRange( image ).levelCount; } uint32_t getArrayLayers( ImageId const & image )noexcept { return image.data->info.arrayLayers; } uint32_t getArrayLayers( ImageViewId const & image )noexcept { return getSubresourceRange( image ).layerCount; } ImageAspectFlags getAspectFlags( ImageViewId const & image )noexcept { return getSubresourceRange( image ).aspectMask; } ImageSubresourceRange const & getSubresourceRange( ImageViewId const & image )noexcept { return image.data->info.subresourceRange; } BufferSubresourceRange const & getSubresourceRange( BufferViewId const & buffer )noexcept { return buffer.data->info.subresourceRange; } AccessFlags getAccessMask( ImageLayout layout )noexcept { AccessFlags result{ 0u }; switch ( layout ) { case ImageLayout::ePresentSrc: case ImageLayout::eSharedPresent: result |= AccessFlags::eMemoryRead; break; case ImageLayout::eColorAttachment: result |= AccessFlags::eColorAttachmentWrite; break; case ImageLayout::eDepthStencilAttachment: result |= AccessFlags::eDepthStencilAttachmentWrite; break; case ImageLayout::eDepthStencilReadOnly: result |= AccessFlags::eDepthStencilAttachmentRead; break; case ImageLayout::eShaderReadOnly: result |= AccessFlags::eShaderRead; break; case ImageLayout::eTransferSrc: result |= AccessFlags::eTransferRead; break; case ImageLayout::eTransferDst: result |= AccessFlags::eTransferWrite; break; case ImageLayout::eDepthReadOnlyStencilAttachment: case ImageLayout::eDepthAttachmentStencilReadOnly: result |= AccessFlags::eDepthStencilAttachmentRead; result |= AccessFlags::eDepthStencilAttachmentWrite; break; #ifdef VK_NV_shading_rate_image case ImageLayout::eFragmentShadingRateAttachment: result |= AccessFlags::eFragmentShadingRateAttachmentRead; break; #endif #ifdef VK_EXT_fragment_density_map case ImageLayout::eFragmentDensityMap: result |= AccessFlags::eFragmentDensityMapRead; break; #endif default: break; } return result; } PipelineState getPipelineState( PipelineStageFlags flags )noexcept { AccessFlags result{ 0u }; if ( checkFlag( flags, PipelineStageFlags::eBottomOfPipe ) ) { result |= AccessFlags::eMemoryRead; } if ( checkFlag( flags, PipelineStageFlags::eColorAttachmentOutput ) ) { result |= AccessFlags::eColorAttachmentWrite | AccessFlags::eColorAttachmentRead; } if ( checkFlag( flags, PipelineStageFlags::eLateFragmentTests ) ) { result |= AccessFlags::eDepthStencilAttachmentWrite; result |= AccessFlags::eDepthStencilAttachmentRead; } if ( checkFlag( flags, PipelineStageFlags::eFragmentShader ) ) { result |= AccessFlags::eShaderRead; } if ( checkFlag( flags, PipelineStageFlags::eTransfer ) ) { result |= AccessFlags::eTransferRead; result |= AccessFlags::eTransferWrite; } if ( checkFlag( flags, PipelineStageFlags::eFragmentShadingRateAttachment ) ) { result |= AccessFlags::eFragmentShadingRateAttachmentRead; } if ( checkFlag( flags, PipelineStageFlags::eComputeShader ) ) { result |= AccessFlags::eShaderRead; } return { result, flags }; } LayoutState makeLayoutState( ImageLayout layout )noexcept { return { layout , getAccessMask( layout ) , getStageMask( layout ) }; } PipelineStageFlags getStageMask( ImageLayout layout )noexcept { PipelineStageFlags result{ 0u }; switch ( layout ) { case ImageLayout::eUndefined: result |= PipelineStageFlags::eHost; break; case ImageLayout::eGeneral: result |= PipelineStageFlags::eBottomOfPipe; break; case ImageLayout::ePresentSrc: case ImageLayout::eSharedPresent: result |= PipelineStageFlags::eBottomOfPipe; break; case ImageLayout::eDepthStencilReadOnly: case ImageLayout::eDepthReadOnlyStencilAttachment: case ImageLayout::eDepthAttachmentStencilReadOnly: case ImageLayout::eDepthStencilAttachment: result |= PipelineStageFlags::eLateFragmentTests; break; case ImageLayout::eColorAttachment: result |= PipelineStageFlags::eColorAttachmentOutput; break; #ifdef VK_EXT_fragment_density_map case ImageLayout::eFragmentDensityMap: #endif case ImageLayout::eShaderReadOnly: result |= PipelineStageFlags::eFragmentShader; break; case ImageLayout::eTransferSrc: case ImageLayout::eTransferDst: result |= PipelineStageFlags::eTransfer; break; #ifdef VK_NV_shading_rate_image case ImageLayout::eFragmentShadingRateAttachment: result |= PipelineStageFlags::eFragmentShadingRateAttachment; break; #endif default: break; } return result; } ImageAspectFlags getAspectMask( PixelFormat format )noexcept { if ( isDepthStencilFormat( format ) ) return ImageAspectFlags::eDepth | ImageAspectFlags::eStencil; if ( isDepthFormat( format ) ) return ImageAspectFlags::eDepth; if ( isStencilFormat( format ) ) return ImageAspectFlags::eStencil; return ImageAspectFlags::eColor; } LayoutState const & addSubresourceRangeLayout( LayerLayoutStates & ranges , ImageSubresourceRange const & range , LayoutState const & newLayout ) { for ( uint32_t layerIdx = 0u; layerIdx < range.layerCount; ++layerIdx ) { auto & layers = ranges.try_emplace( range.baseArrayLayer + layerIdx ).first->second; for ( uint32_t levelIdx = 0u; levelIdx < range.levelCount; ++levelIdx ) { layers.insert_or_assign( range.baseMipLevel + levelIdx, newLayout ); } } return newLayout; } static void gatherSubresourceRangeLayoutMips( ImageSubresourceRange const & range , MipLayoutStates const & layers , std::map< ImageLayout, LayoutState > & states ) { for ( uint32_t levelIdx = 0u; levelIdx < range.levelCount; ++levelIdx ) { if ( auto it = layers.find( range.baseMipLevel + levelIdx ); it != layers.end() ) { auto state = it->second; auto [rit, res] = states.emplace( state.layout, state ); if ( !res ) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnull-dereference" rit->second.state.access |= state.state.access; #pragma GCC diagnostic pop } } } } LayoutState getSubresourceRangeLayout( LayerLayoutStates const & ranges , ImageSubresourceRange const & range ) { std::map< ImageLayout, LayoutState > states; for ( uint32_t layerIdx = 0u; layerIdx < range.layerCount; ++layerIdx ) { if ( auto layerIt = ranges.find( range.baseArrayLayer + layerIdx ); layerIt != ranges.end() ) { auto & layers = layerIt->second; gatherSubresourceRangeLayoutMips( range, layers, states ); } } if ( states.empty() ) { return { ImageLayout::eUndefined , getAccessMask( ImageLayout::eUndefined ) , getStageMask( ImageLayout::eUndefined ) }; } return states.begin()->second; } ImageSubresourceRange getVirtualRange( ImageId const & image , ImageViewType viewType , ImageSubresourceRange const & range )noexcept { ImageSubresourceRange result = range; if ( viewType == ImageViewType::e3D && ( range.levelCount == 1u || range.levelCount == image.data->info.mipLevels ) ) { result.baseArrayLayer = 0u; result.layerCount = getExtent( image ).depth >> range.baseMipLevel; } return result; } bool match( ImageViewId const & lhs, ImageViewId const & rhs )noexcept { return fgph::match( *lhs.data, *rhs.data ); } bool match( BufferViewId const & lhs, BufferViewId const & rhs )noexcept { return lhs == rhs; } ImageViewId const & resolveView( ImageViewId const & view , uint32_t passIndex ) { return view.data->source.empty() ? view : view.data->source[passIndex]; } BufferViewId const & resolveView( BufferViewId const & view , uint32_t passIndex ) { return view.data->source.empty() ? view : view.data->source[passIndex]; } ClearColorValue getClearColorValue( ClearValue const & v ) { if ( v.isColor() ) return v.color(); static ClearColorValue dummy{}; return dummy; } ClearDepthStencilValue getClearDepthStencilValue( ClearValue const & v ) { if ( v.isDepthStencil() ) return v.depthStencil(); static ClearDepthStencilValue dummy{}; return dummy; } //********************************************************************************************* } ================================================ FILE: source/RenderGraph/FramePass.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/FramePass.hpp" #include "RenderGraph/FrameGraph.hpp" #include "RenderGraph/RunnablePass.hpp" #include namespace crg { inline uint32_t constexpr ImplicitOffset = 1024U; inline uint32_t constexpr TransferOffset = 4096U; namespace fpass { static std::string adjustName( FramePass const & pass , std::string const & dataName ) { auto result = pass.getGroupName() + "/" + dataName; uint32_t index = 0u; while ( result[index] == '/' ) { ++index; } return result.substr( index ); } } FramePass::FramePass( FramePassGroup const & group , FrameGraph & graph , uint32_t id , std::string const & name , RunnablePassCreator runnableCreator ) : m_group{ group } , m_graph{ graph } , m_id{ id } , m_runnableCreator{ std::move( runnableCreator ) } , m_name{ name } { } Attachment const * FramePass::getParentAttachment( Attachment const & attach )const { auto it = m_ownAttaches.find( &attach ); return it != m_ownAttaches.end() ? it->second.parent : nullptr; } void FramePass::addInputUniformBuffer( BufferViewIdArray buffers , uint32_t binding ) { auto attachName = fpass::adjustName( *this, buffers.front().data->name ) + "/UB"; auto attach = addOwnAttach( std::move( buffers ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Uniform ) , AccessState{} , nullptr ); m_uniforms.try_emplace( binding, attach ); } void FramePass::addInputSampledImage( ImageViewIdArray views , uint32_t binding , SamplerDesc samplerDesc ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/Spl"; auto attach = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Sampled ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eShaderReadOnly , nullptr ); m_sampled.try_emplace( binding, attach, std::move( samplerDesc ) ); } void FramePass::addInputUniform( Attachment const & attachment , uint32_t binding ) { auto attachName = fpass::adjustName( *this, attachment.buffer().data->name ) + "/UB"; auto attach = addOwnAttach( attachment.bufferAttach.buffers , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Uniform ) , AccessState{} , &attachment ); m_uniforms.try_emplace( binding, attach ); } void FramePass::addInputSampled( Attachment const & attachment , uint32_t binding , SamplerDesc samplerDesc ) { auto attachName = fpass::adjustName( *this, attachment.view( 0 ).data->name ) + "/Spl"; auto attach = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Sampled ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eShaderReadOnly , &attachment ); m_sampled.try_emplace( binding, attach, std::move( samplerDesc ) ); } void FramePass::addInputStorageBuffer( BufferViewIdArray buffers , uint32_t binding ) { auto attachName = fpass::adjustName( *this, buffers.front().data->name ) + "/SB"; auto attach = addOwnAttach( std::move( buffers ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Storage ) , AccessState{} , nullptr ); m_inputs.try_emplace( binding, attach ); } void FramePass::addInputStorageImage( ImageViewIdArray views , uint32_t binding ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/IStr"; auto attach = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Storage ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eGeneral , nullptr ); m_inputs.try_emplace( binding, attach ); } void FramePass::addInputStorage( Attachment const & attachment , uint32_t binding ) { if ( attachment.isImage() ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IStr"; auto attach = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Storage ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eGeneral , &attachment ); m_inputs.try_emplace( binding, attach ); } else { auto attachName = fpass::adjustName( *this, attachment.buffer().data->name ) + "/IStr"; auto attach = addOwnAttach( attachment.bufferAttach.buffers , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Storage ) , AccessState{} , &attachment ); m_inputs.try_emplace( binding, attach ); } } Attachment const * FramePass::addInOutStorage( Attachment const & attachment , uint32_t binding ) { Attachment const * result{}; if ( attachment.isImage() ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IOStr"; result = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::InOut ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Storage ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eGeneral , &attachment ); m_inouts.try_emplace( binding, result ); } else { auto attachName = fpass::adjustName( *this, attachment.buffer().data->name ) + "/IOStr"; result = addOwnAttach( attachment.bufferAttach.buffers , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::InOut ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Storage ) , AccessState{} , &attachment ); m_inouts.try_emplace( binding, result ); } return result; } Attachment const * FramePass::addOutputStorageBuffer( BufferViewIdArray buffers , uint32_t binding ) { auto attachName = fpass::adjustName( *this, buffers.front().data->name ) + "/OSB"; auto result = addOwnAttach( std::move( buffers ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Output ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Storage ) , AccessState{} , nullptr ); m_outputs.try_emplace( binding, result ); return result; } Attachment const * FramePass::addClearableOutputStorageBuffer( BufferViewIdArray buffers , uint32_t binding ) { auto attachName = fpass::adjustName( *this, buffers.front().data->name ) + "/OSB"; auto result = addOwnAttach( std::move( buffers ) , std::move( attachName ) , ( Attachment::FlagKind( Attachment::Flag::Output ) | Attachment::FlagKind( Attachment::Flag::Clearable ) ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Storage ) , AccessState{} , nullptr ); m_outputs.try_emplace( binding, result ); return result; } Attachment const * FramePass::addOutputStorageImage( ImageViewIdArray views , uint32_t binding ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/OStr"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Output ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Storage ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eGeneral , nullptr ); m_outputs.try_emplace( binding, result ); return result; } Attachment const * FramePass::addClearableOutputStorageImage( ImageViewIdArray views , uint32_t binding , ClearValue clearValue ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/COStr"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , ( Attachment::FlagKind( Attachment::Flag::Output ) | Attachment::FlagKind( Attachment::Flag::Clearable ) ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Storage ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , std::move( clearValue ) , PipelineColorBlendAttachmentState{} , ImageLayout::eGeneral , nullptr ); m_outputs.try_emplace( binding, result ); return result; } void FramePass::addInputTransferBuffer( BufferViewIdArray views ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/ITrf"; auto attach = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Transfer ) , AccessState{} , nullptr ); m_inputs.try_emplace( TransferOffset + uint32_t( m_inputs.size() ), attach ); } void FramePass::addInputTransferImage( ImageViewIdArray views ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/ITrf"; auto attach = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Transfer ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eTransferSrc , nullptr ); m_inputs.try_emplace( TransferOffset + uint32_t( m_inputs.size() ), attach ); } void FramePass::addInputTransfer( Attachment const & attachment ) { if ( attachment.isImage() ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/ITrf"; auto attach = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Transfer ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eTransferSrc , & attachment ); m_inputs.try_emplace( TransferOffset + uint32_t( m_inputs.size() ), attach ); } else { auto attachName = fpass::adjustName( *this, attachment.buffer().data->name ) + "/ITrf"; auto attach = addOwnAttach( attachment.bufferAttach.buffers , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Transfer ) , AccessState{} , &attachment ); m_inputs.try_emplace( TransferOffset + uint32_t( m_inputs.size() ), attach ); } } Attachment const * FramePass::addInOutTransfer( Attachment const & attachment , Attachment::Flag flag ) { Attachment const * result{}; if ( attachment.isImage() ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IOTrf"; result = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::FlagKind( Attachment::Flag::InOut ) | Attachment::FlagKind( flag ) ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Transfer ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eTransferSrc , &attachment ); m_inouts.try_emplace( TransferOffset + uint32_t( m_inouts.size() ), result ); } else { auto attachName = fpass::adjustName( *this, attachment.buffer().data->name ) + "/IOTrf"; result = addOwnAttach( attachment.bufferAttach.buffers , std::move( attachName ) , Attachment::FlagKind( Attachment::FlagKind( Attachment::Flag::InOut ) | Attachment::FlagKind( flag ) ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Transfer ) , AccessState{} , &attachment ); m_inouts.try_emplace( TransferOffset + uint32_t( m_inouts.size() ), result ); } return result; } Attachment const * FramePass::addOutputTransferBuffer( BufferViewIdArray buffers ) { auto attachName = fpass::adjustName( *this, buffers.front().data->name ) + "/OTB"; auto result = addOwnAttach( std::move( buffers ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Output ) , BufferAttachment::FlagKind( BufferAttachment::Flag::Transfer ) , AccessState{} , nullptr ); m_outputs.try_emplace( TransferOffset + uint32_t( m_outputs.size() ), result ); return result; } Attachment const * FramePass::addOutputTransferImage( ImageViewIdArray views ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/OT"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Output ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Transfer ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , ImageLayout::eTransferDst , nullptr ); m_outputs.try_emplace( TransferOffset + uint32_t( m_outputs.size() ), result ); return result; } void FramePass::addInputColourTargetImage( ImageViewIdArray views ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/IRcl"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::None ) , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{ ClearColorValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eColorAttachment , nullptr ); m_targets.emplace_back( result ); } void FramePass::addInputDepthTargetImage( ImageViewIdArray views ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/IRdp"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Depth ) , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{ ClearDepthStencilValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , nullptr ); m_targets.emplace_back( result ); } void FramePass::addInputStencilTargetImage( ImageViewIdArray views ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/IRst"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::StencilInput ) | ImageAttachment::FlagKind( ImageAttachment::Flag::Stencil ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , ClearValue{ ClearDepthStencilValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , nullptr ); m_targets.emplace_back( result ); } void FramePass::addInputDepthStencilTargetImage( ImageViewIdArray views ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/IRds"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::StencilInput ) | ImageAttachment::FlagKind( ImageAttachment::Flag::DepthStencil ) , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , ClearValue{ ClearDepthStencilValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , nullptr ); m_targets.emplace_back( result ); } void FramePass::addInputColourTarget( Attachment const & attachment ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IRcl"; auto result = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::None ) , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{ ClearColorValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eColorAttachment , &attachment ); m_targets.emplace_back( result ); } void FramePass::addInputDepthTarget( Attachment const & attachment ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IRdp"; auto attach = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Depth ) , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{ ClearDepthStencilValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , &attachment ); m_targets.emplace_back( attach ); } void FramePass::addInputStencilTarget( Attachment const & attachment ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IRst"; auto attach = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::StencilInput ) | ImageAttachment::FlagKind( ImageAttachment::Flag::Stencil ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , ClearValue{ ClearDepthStencilValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , &attachment ); m_targets.emplace_back( attach ); } void FramePass::addInputDepthStencilTarget( Attachment const & attachment ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IRds"; auto attach = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::Flag::StencilInput ) | ImageAttachment::FlagKind( ImageAttachment::Flag::DepthStencil ) , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eLoad, AttachmentStoreOp::eDontCare , ClearValue{ ClearDepthStencilValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , &attachment ); m_targets.emplace_back( attach ); } Attachment const * FramePass::addInOutColourTarget( Attachment const & attachment , PipelineColorBlendAttachmentState blendState ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IORcl"; auto result = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::InOut ) , ImageAttachment::FlagKind( ImageAttachment::Flag::None ) , AttachmentLoadOp::eLoad, AttachmentStoreOp::eStore , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{ ClearColorValue{} } , std::move( blendState ) , ImageLayout::eColorAttachment , &attachment ); m_targets.emplace_back( result ); return result; } Attachment const * FramePass::addInOutDepthTarget( Attachment const & attachment ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IORdp"; auto result = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::InOut ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Depth ) , AttachmentLoadOp::eLoad, AttachmentStoreOp::eStore , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{ ClearDepthStencilValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , &attachment ); m_targets.emplace_back( result ); return result; } Attachment const * FramePass::addInOutStencilTarget( Attachment const & attachment ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IORst"; auto result = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::InOut ) , ImageAttachment::FlagKind( ImageAttachment::Flag::StencilInOut ) | ImageAttachment::FlagKind( ImageAttachment::Flag::Stencil ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eLoad, AttachmentStoreOp::eStore , ClearValue{ ClearDepthStencilValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , &attachment ); m_targets.emplace_back( result ); return result; } Attachment const * FramePass::addInOutDepthStencilTarget( Attachment const & attachment ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/IORds"; auto result = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::InOut ) , ImageAttachment::FlagKind( ImageAttachment::Flag::StencilInOut ) | ImageAttachment::FlagKind( ImageAttachment::Flag::DepthStencil ) , AttachmentLoadOp::eLoad, AttachmentStoreOp::eStore , AttachmentLoadOp::eLoad, AttachmentStoreOp::eStore , ClearValue{ ClearDepthStencilValue{} } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , &attachment ); m_targets.emplace_back( result ); return result; } Attachment const * FramePass::addOutputColourTarget( ImageViewIdArray views , ClearColorValue clearValue ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/ORcl"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Output ) , ImageAttachment::FlagKind( ImageAttachment::Flag::None ) , AttachmentLoadOp::eClear, AttachmentStoreOp::eStore , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{ std::move( clearValue ) } , PipelineColorBlendAttachmentState{} , ImageLayout::eColorAttachment , nullptr ); m_targets.emplace_back( result ); return result; } Attachment const * FramePass::addOutputDepthTarget( ImageViewIdArray views , ClearDepthStencilValue clearValue ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/ORdp"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Output ) , ImageAttachment::FlagKind( ImageAttachment::Flag::Depth ) , AttachmentLoadOp::eClear, AttachmentStoreOp::eStore , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{ std::move( clearValue ) } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthAttachment , nullptr ); m_targets.emplace( m_targets.begin(), result ); return result; } Attachment const * FramePass::addOutputStencilTarget( ImageViewIdArray views , ClearDepthStencilValue clearValue ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/ORst"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Output ) , ImageAttachment::FlagKind( ImageAttachment::FlagKind( ImageAttachment::Flag::StencilOutput ) | ImageAttachment::FlagKind( ImageAttachment::Flag::Stencil ) ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eClear, AttachmentStoreOp::eStore , ClearValue{ std::move( clearValue ) } , PipelineColorBlendAttachmentState{} , ImageLayout::eStencilAttachment , nullptr ); m_targets.emplace( m_targets.begin(), result ); return result; } Attachment const * FramePass::addOutputDepthStencilTarget( ImageViewIdArray views , ClearDepthStencilValue clearValue ) { auto attachName = fpass::adjustName( *this, views.front().data->name ) + "/ORds"; auto result = addOwnAttach( std::move( views ) , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Output ) , ImageAttachment::FlagKind( ImageAttachment::FlagKind( ImageAttachment::Flag::StencilOutput ) | ImageAttachment::FlagKind( ImageAttachment::Flag::DepthStencil ) ) , AttachmentLoadOp::eClear, AttachmentStoreOp::eStore , AttachmentLoadOp::eClear, AttachmentStoreOp::eStore , ClearValue{ std::move( clearValue ) } , PipelineColorBlendAttachmentState{} , ImageLayout::eDepthStencilAttachment , nullptr ); m_targets.emplace( m_targets.begin(), result ); return result; } void FramePass::addImplicit( Attachment const & attachment , AccessState wantedAccess ) { auto attachName = fpass::adjustName( *this, attachment.buffer().data->name ) + "/Impl"; auto attach = addOwnAttach( attachment.bufferAttach.buffers , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , BufferAttachment::FlagKind( BufferAttachment::FlagKind( BufferAttachment::Flag::Transition ) | attachment.bufferAttach.getFormatFlags() ) , std::move( wantedAccess ) , &attachment ); m_inputs.try_emplace( ImplicitOffset + uint32_t( m_inputs.size() ), attach ); } void FramePass::addImplicit( Attachment const & attachment , ImageLayout wantedLayout ) { auto attachName = fpass::adjustName( *this, attachment.view().data->name ) + "/Impl"; auto attach = addOwnAttach( attachment.imageAttach.views , std::move( attachName ) , Attachment::FlagKind( Attachment::Flag::Input ) , ImageAttachment::FlagKind( ImageAttachment::FlagKind( ImageAttachment::Flag::Transition ) | attachment.imageAttach.getFormatFlags() ) , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , AttachmentLoadOp::eDontCare, AttachmentStoreOp::eDontCare , ClearValue{} , PipelineColorBlendAttachmentState{} , wantedLayout , &attachment ); m_inputs.try_emplace( ImplicitOffset + uint32_t( m_inputs.size() ), attach ); } RunnablePassPtr FramePass::createRunnable( GraphContext & context , RunnableGraph & pgraph )const { return m_runnableCreator( *this, context, pgraph ); } std::string FramePass::getFullName()const { return m_group.getFullName() + "/" + getName(); } std::string FramePass::getGroupName()const { return m_group.getName() + "/" + getName(); } Attachment const * FramePass::addOwnAttach( ImageViewIdArray views, std::string attachName , Attachment::FlagKind flags, ImageAttachment::FlagKind imageFlags , AttachmentLoadOp loadOp, AttachmentStoreOp storeOp , AttachmentLoadOp stencilLoadOp, AttachmentStoreOp stencilStoreOp , ClearValue clearValue, PipelineColorBlendAttachmentState blendState , ImageLayout wantedLayout , Attachment const * parent ) { if ( views.front().data->source.empty() ) return addOwnAttach( new Attachment{ flags , *this, std::move( attachName ) , imageFlags , std::move( views ) , loadOp, storeOp , stencilLoadOp, stencilStoreOp , std::move( clearValue ) , std::move( blendState ) , wantedLayout , Attachment::Token{} } , parent ); // Dispatch merged views sources in source attachs views std::vector< ImageViewIdArray > sourceAttachsViews; sourceAttachsViews.resize( views.front().data->source.size() ); for ( auto view : views ) { for ( uint32_t i = 0u; i < view.data->source.size(); ++i ) sourceAttachsViews[i].push_back( view.data->source[i] ); } // Use these views to create attachs uint32_t index{}; std::vector< AttachmentPtr > sources; for ( auto & sourceViews : sourceAttachsViews ) { sources.push_back( std::make_unique< Attachment >( flags , *this, attachName + std::to_string( index ) , imageFlags , std::move( sourceViews ) , loadOp, storeOp , stencilLoadOp, stencilStoreOp , clearValue, blendState , wantedLayout , Attachment::Token{} ) ); ++index; } // Create the resulting attach auto result = addOwnAttach( new Attachment{ flags , *this, std::move( attachName ) , imageFlags , std::move( views ) , loadOp, storeOp , stencilLoadOp, stencilStoreOp , std::move( clearValue ) , std::move( blendState ) , wantedLayout , Attachment::Token{} } , parent ); result->pass = nullptr; // And set its sources if ( !parent || parent->source.empty() ) { for ( auto & sourceAttach : sources ) result->source.emplace_back( std::move( sourceAttach ) ); } else { // If parent has sources, link the new attachment sources to the parent ones assert( parent->source.size() == sources.size() ); for ( uint32_t i = 0; i < sources.size(); ++i ) { auto source = addOwnAttach( sources[i].release() , ( parent->source[i].parent ? parent->source[i].parent : parent->source[i].attach.get() ) ); result->source.emplace_back( parent->source[i].attach.get(), source->pass, source->imageAttach ); } result->initSources(); } return result; } Attachment const * FramePass::addOwnAttach( BufferViewIdArray views, std::string attachName , Attachment::FlagKind flags, BufferAttachment::FlagKind bufferFlags , AccessState access , Attachment const * parent ) { if ( views.front().data->source.empty() ) return addOwnAttach( new Attachment{ flags , *this, std::move( attachName ) , bufferFlags , std::move( views ) , std::move( access ) , Attachment::Token{} } , parent ); // Dispatch merged views sources in source attachs views std::vector< BufferViewIdArray > sourceAttachsViews; sourceAttachsViews.resize( views.front().data->source.size() ); for ( auto view : views ) { for ( uint32_t i = 0u; i < view.data->source.size(); ++i ) sourceAttachsViews[i].push_back( view.data->source[i] ); } // Use these views to create attachs uint32_t index{}; std::vector< AttachmentPtr > sources; for ( auto & sourceViews : sourceAttachsViews ) { sources.push_back( std::make_unique< Attachment >( flags , *this, attachName + std::to_string( index ) , bufferFlags , std::move( sourceViews ) , access , Attachment::Token{} ) ); ++index; } // Create the resulting attach auto result = addOwnAttach( new Attachment{ flags , *this, std::move( attachName ) , bufferFlags , std::move( views ) , std::move( access ) , Attachment::Token{} } , parent ); result->pass = nullptr; // And set its sources if ( !parent || parent->source.empty() ) { for ( auto & sourceAttach : sources ) result->source.emplace_back( std::move( sourceAttach ) ); } else { // If parent has sources, link the new attachment sources to the parent ones assert( parent->source.size() == sources.size() ); for ( uint32_t i = 0; i < sources.size(); ++i ) { auto source = addOwnAttach( sources[i].release() , ( parent->source[i].parent ? parent->source[i].parent : parent->source[i].attach.get() ) ); result->source.emplace_back( parent->source[i].attach.get(), source->pass, source->bufferAttach ); } result->initSources(); } return result; } Attachment * FramePass::addOwnAttach( Attachment * mine , Attachment const * parent ) { auto & own = m_ownAttaches.try_emplace( mine ).first->second; own.mine.reset( mine ); own.parent = parent; return mine; } } ================================================ FILE: source/RenderGraph/FramePassGroup.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/FramePassGroup.hpp" #include "RenderGraph/Exception.hpp" #include "RenderGraph/Log.hpp" #include "RenderGraph/FrameGraph.hpp" #include #include namespace crg { namespace group { static FramePassGroup const * getOutermost( FramePassGroup const * group ) { while ( group && group->getParent() ) group = group->getParent(); return group; } static uint32_t countPasses( FramePassGroup const * group ) { return std::accumulate( group->getGroups().begin() , group->getGroups().end() , uint32_t( group->getPasses().size() ) , []( uint32_t val, FramePassGroupPtr const & lookup ) { return val + countPasses( lookup.get() ); } ); } static uint32_t countGroups( FramePassGroup const * group ) { return std::accumulate( group->getGroups().begin() , group->getGroups().end() , uint32_t( group->getGroups().size() ) , []( uint32_t val, FramePassGroupPtr const & lookup ) { return val + countGroups( lookup.get() ); } ); } } FramePassGroup::FramePassGroup( FrameGraph & graph , uint32_t pid , std::string const & name , Token ) : m_id{ pid } , m_name{ name } , m_graph{ graph } { } FramePassGroup::FramePassGroup( FramePassGroup & pparent , uint32_t pid , std::string const & name , Token ) : m_id{ pid } , m_parent{ &pparent } , m_name{ name } , m_graph{ m_parent->m_graph } { } FramePass & FramePassGroup::createPass( std::string const & passName , RunnablePassCreator runnableCreator ) { if ( hasPass( passName ) ) { Logger::logWarning( "Duplicate FramePass name detected." ); CRG_Exception( "Duplicate FramePass name detected." ); } auto count = group::countPasses( group::getOutermost( this ) ); m_passes.emplace_back( new FramePass{ *this , m_graph , count + 1u , passName , std::move( runnableCreator ) } ); return *m_passes.back(); } FramePassGroup & FramePassGroup::createPassGroup( std::string const & groupName ) { auto it = std::find_if( m_groups.begin() , m_groups.end() , [&groupName]( FramePassGroupPtr const & lookup ) { return lookup->getName() == groupName; } ); if ( it == m_groups.end() ) { auto count = group::countGroups( group::getOutermost( this ) ); m_groups.emplace_back( std::make_unique< FramePassGroup >( *this, count + 1u, groupName, Token{} ) ); it = std::next( m_groups.begin(), ptrdiff_t( m_groups.size() - 1u ) ); } return **it; } bool FramePassGroup::hasPass( std::string const & passName )const { return m_passes.end() != std::find_if( m_passes.begin() , m_passes.end() , [&passName]( FramePassPtr const & lookup ) { return lookup->getName() == passName; } ); } void FramePassGroup::listPasses( FramePassArray & result )const { for ( auto & pass : m_passes ) { result.push_back( pass.get() ); } for ( auto & group : m_groups ) { group->listPasses( result ); } } void FramePassGroup::addGroupInput( ImageViewId view ) { m_inputs.emplace( view.id ); } void FramePassGroup::addGroupOutput( ImageViewId view ) { m_outputs.emplace( view.id ); } LayoutState FramePassGroup::getFinalLayoutState( ImageViewId view , uint32_t passIndex )const { return m_graph.getFinalLayoutState( view, passIndex ); } BufferId FramePassGroup::createBuffer( BufferData const & img )const { return m_graph.createBuffer( img ); } BufferViewId FramePassGroup::createView( BufferViewData const & view )const { return m_graph.createView( view ); } ImageId FramePassGroup::createImage( ImageData const & img )const { return m_graph.createImage( img ); } ImageViewId FramePassGroup::createView( ImageViewData const & view )const { return m_graph.createView( view ); } void FramePassGroup::addInput( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range , LayoutState const & outputLayout ) { m_graph.addInput( image , viewType , range , outputLayout ); } void FramePassGroup::addInput( ImageViewId view , LayoutState const & outputLayout ) { addInput( view.data->image , view.data->info.viewType , view.data->info.subresourceRange , outputLayout ); } void FramePassGroup::addOutput( ImageId image , ImageViewType viewType , ImageSubresourceRange const & range , LayoutState const & outputLayout ) { m_graph.addOutput( image , viewType , range , outputLayout ); } void FramePassGroup::addOutput( ImageViewId view , LayoutState const & outputLayout ) { addOutput( view.data->image , view.data->info.viewType , view.data->info.subresourceRange , outputLayout ); } std::string FramePassGroup::getFullName()const { return ( &m_graph.getDefaultGroup() == this ) ? m_graph.getName() : m_parent->getFullName() + "/" + getName(); } ImageViewId FramePassGroup::mergeViews( ImageViewIdArray const & views , bool mergeMipLevels , bool mergeArrayLayers ) { return m_graph.mergeViews( views , mergeMipLevels , mergeArrayLayers ); } BufferViewId FramePassGroup::mergeViews( BufferViewIdArray const & views ) { return m_graph.mergeViews( views ); } Attachment const * FramePassGroup::mergeAttachments( AttachmentArray const & attachments , bool mergeMipLevels , bool mergeArrayLayers ) { return m_graph.mergeAttachments( attachments , mergeMipLevels , mergeArrayLayers ); } } ================================================ FILE: source/RenderGraph/FramePassTimer.cpp ================================================ #include "RenderGraph/FramePassTimer.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/Log.hpp" #include namespace crg { using namespace std::literals::chrono_literals; //********************************************************************************************* FramePassTimerBlock::FramePassTimerBlock( FramePassTimer & timer ) : m_timer{ &timer } { } FramePassTimerBlock::FramePassTimerBlock( FramePassTimerBlock && block )noexcept : m_timer{ block.m_timer } { block.m_timer = {}; } FramePassTimerBlock::~FramePassTimerBlock()noexcept { if ( m_timer ) { m_timer->stop(); } } //********************************************************************************************* FramePassTimer::FramePassTimer( GraphContext & context , std::string const & name , TimerScope scope , VkQueryPool timerQueries , uint32_t & baseQueryOffset ) : m_context{ context } , m_scope{ scope } , m_name{ name } , m_colour{ context.getNextRainbowColour() } , m_timerQueries{ timerQueries } , m_queries{ { { baseQueryOffset, false, false }, { baseQueryOffset + 2u, false, false } } } { baseQueryOffset += 4u; } FramePassTimer::FramePassTimer( GraphContext & context , std::string const & name , TimerScope scope ) : m_context{ context } , m_scope{ scope } , m_name{ name } , m_colour{ context.getNextRainbowColour() } , m_timerQueries{ createQueryPool( context, name, 4u ) } , m_ownPool{ true } , m_queries{ { { 0u, false, false }, { 2u, false, false } } } { } FramePassTimer::~FramePassTimer()noexcept { try { onDestroy( *this ); if ( m_ownPool && m_timerQueries ) { crgUnregisterObject( m_context, m_timerQueries ); m_context.vkDestroyQueryPool( m_context.device , m_timerQueries , m_context.allocator ); } } catch ( ... ) { // Nothing to do here } } FramePassTimerBlock FramePassTimer::start() { m_cpuSaveTime = Clock::now(); return FramePassTimerBlock{ *this }; } void FramePassTimer::notifyPassRender( [[maybe_unused]] uint32_t passIndex )noexcept { auto & query = m_queries.front(); query.started = true; } void FramePassTimer::stop()noexcept { auto current = Clock::now(); m_cpuTime += ( current - m_cpuSaveTime ); } void FramePassTimer::reset()noexcept { m_cpuTime = 0ns; m_gpuTime = 0ns; } void FramePassTimer::beginPass( VkCommandBuffer commandBuffer , std::string const & groupName , uint32_t passId )noexcept { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wrestrict" m_context.vkCmdBeginDebugBlock( commandBuffer , { "[" + std::to_string( passId ) + "] " + groupName , m_colour } ); #pragma GCC diagnostic pop std::swap( m_queries.front(), m_queries.back() ); auto const & query = m_queries.front(); m_context.vkCmdResetQueryPool( commandBuffer , m_timerQueries , query.offset , 2u ); m_context.vkCmdWriteTimestamp( commandBuffer , VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT , m_timerQueries , query.offset + 0u ); } void FramePassTimer::endPass( VkCommandBuffer commandBuffer )noexcept { auto & query = m_queries.front(); m_context.vkCmdWriteTimestamp( commandBuffer , VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT , m_timerQueries , query.offset + 1u ); query.written = true; m_context.vkCmdEndDebugBlock( commandBuffer ); } void FramePassTimer::retrieveGpuTime()noexcept { static float const period = m_context.properties.limits.timestampPeriod; auto before = Clock::now(); m_gpuTime = 0ns; if ( auto & query = m_queries.front(); query.started && query.written ) { std::array< uint64_t, 2u > values{ 0u, 0u }; m_context.vkGetQueryPoolResults( m_context.device , m_timerQueries , query.offset , 2u , sizeof( uint64_t ) * values.size() , values.data() , sizeof( uint64_t ) , VK_QUERY_RESULT_WAIT_BIT | VK_QUERY_RESULT_64_BIT ); auto gpuTime = Nanoseconds{ uint64_t( float( values[1] - values[0] ) / period ) }; m_gpuTime += gpuTime; query.started = false; query.written = false; } auto after = Clock::now(); m_cpuTime += ( after - before ); } //********************************************************************************************* } ================================================ FILE: source/RenderGraph/GraphBuilder.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "GraphBuilder.hpp" #include "RenderGraph/FramePass.hpp" #include "RenderGraph/GraphNode.hpp" #include "RenderGraph/Log.hpp" #include namespace crg::builder { //********************************************************************************************* namespace endpoints { static void addAttach( Attachment const & attach , AttachmentArray & result ) { if ( attach.source.empty() ) result.push_back( &attach ); for ( auto & source : attach.source ) result.push_back( source.attach.get() ); } static void listAllAttachs( FramePassArray const & passes , AttachmentArray & result ) { for ( auto pass : passes ) { for ( auto & [binding, attach] : pass->getInouts() ) addAttach( *attach, result ); for ( auto & [binding, attach] : pass->getOutputs() ) addAttach( *attach, result ); for ( auto & attach : pass->getTargets() ) if ( attach->isOutput() ) addAttach( *attach, result ); } } static void listBuffersRec( Attachment const & attach , FramePass const * pass , BufferViewIdArray & result ) { if ( attach.source.empty() && ( !pass || attach.pass == pass ) ) for ( auto & view : attach.bufferAttach.buffers ) result.push_back( view ); for ( auto const & source : attach.source ) listBuffersRec( *source.attach, pass, result ); } static BufferViewIdArray listBuffers( Attachment const & attach , FramePass const * pass ) { BufferViewIdArray result; listBuffersRec( attach, pass, result ); return result; } static void listImagesRec( Attachment const & attach , FramePass const * pass , ImageViewIdArray & result ) { if ( attach.source.empty() && ( !pass || attach.pass == pass ) ) for ( auto & view : attach.imageAttach.views ) result.push_back( view ); for ( auto const & source : attach.source ) listImagesRec( *source.attach, pass, result ); } static ImageViewIdArray listImages( Attachment const & attach , FramePass const * pass ) { ImageViewIdArray result; listImagesRec( attach, pass, result ); return result; } static bool isBufferAttachParent( Attachment const & parent, Attachment const & attach ) { auto parentBuffers = listBuffers( parent, attach.pass ); auto attachBuffers = listBuffers( attach, nullptr ); return std::any_of( attachBuffers.begin(), attachBuffers.end() , [&parentBuffers]( BufferViewId const & lookup ) { return parentBuffers.end() != std::find( parentBuffers.begin(), parentBuffers.end(), lookup ); } ); } static bool isImageAttachParent( Attachment const & parent, Attachment const & attach ) { auto parentBuffers = listImages( parent, attach.pass ); auto attachBuffers = listImages( attach, nullptr ); return std::any_of( attachBuffers.begin(), attachBuffers.end() , [&parentBuffers]( ImageViewId const & lookup ) { return parentBuffers.end() != std::find( parentBuffers.begin(), parentBuffers.end(), lookup ); } ); } static bool isAttachParent( Attachment const * parent, Attachment const * attach ) { if ( bool result = ( parent == attach ); result || !parent ) return result; if ( attach->isBuffer() != parent->isBuffer() ) return false; if ( attach->isBuffer() ) return isBufferAttachParent( *parent, *attach ); return isImageAttachParent( *parent, *attach ); } static bool isParentAttachForPass( Attachment const * attach, FramePass const * pass ) { return pass->end() != std::find_if( pass->begin(), pass->end() , [&attach]( auto const & lookup ) { return isAttachParent( lookup.second.parent, attach ); } ); } static void listNonParentAttachs( FramePassArray const & passes , AttachmentArray const & allAttachs , AttachmentArray & result ) { for ( auto attach : allAttachs ) { if ( std::none_of( passes.begin(), passes.end() , [&attach]( FramePass const * pass ) { return attach->pass != pass && isParentAttachForPass( attach, pass ); } ) ) { addAttach( *attach, result ); } } } static bool hasOutput( FramePass const & pass ) { auto result = ( !pass.getOutputs().empty() || !pass.getInouts().empty() ); if ( !result ) { for ( auto & attach : pass.getTargets() ) result = result || attach->isOutput(); } return result; } static void addPassInputs( FramePass const & pass , AttachmentArray & result ) { for ( auto & [_, attach] : pass.getInputs() ) addAttach( *attach, result ); for ( auto & [_, attach] : pass.getUniforms() ) addAttach( *attach, result ); for ( auto & [_, attach] : pass.getSampled() ) result.push_back( attach.attach ); for ( auto & attach : pass.getTargets() ) if ( attach->isInput() ) addAttach( *attach, result ); } static void addSinkPassInputs( FramePassArray const & passes , AttachmentArray & result ) { for ( auto & pass : passes ) if ( !hasOutput( *pass ) ) { addPassInputs( *pass, result ); } } static AttachmentArray listPassOutputs( FramePass const & pass ) { AttachmentArray result; for ( auto & [_, attach] : pass.getOutputs() ) addAttach( *attach, result ); for ( auto & [_, attach] : pass.getInouts() ) addAttach( *attach, result ); for ( auto & attach : pass.getTargets() ) if ( attach->isOutput() ) addAttach( *attach, result ); return result; } static bool areAllPassAttachsListed( FramePass const & pass , AttachmentArray const & result ) { auto passOutputs = listPassOutputs( pass ); return std::all_of( passOutputs.begin(), passOutputs.end() , [&result]( Attachment const * lookup ) { return result.end() != std::find( result.begin(), result.end(), lookup ); } ); } static void removeMismatchs( AttachmentArray & result ) { auto it = result.begin(); while ( it != result.end() ) { if ( !areAllPassAttachsListed( *( *it )->pass, result ) ) it = result.erase( it ); else ++it; } } } //********************************************************************************************* namespace graph { struct AttachmentStates { bool separateDepthStencilLayouts; std::vector< ImageLayout > imageStates{}; std::vector< AccessFlags > bufferStates{}; }; static FramePassArray listAttachmentPasses( Attachment const & attach ) { if ( attach.pass ) return { attach.pass }; FramePassArray result; for ( auto const & source : attach.source ) result.push_back( source.pass ); return result; } static AttachmentArray splitImage( Attachment const & attach ) { assert( attach.view().data->source.empty() ); return { &attach }; } static AttachmentArray splitBuffer( Attachment const & attach ) { assert( attach.buffer().data->source.empty() ); return { &attach }; } static AttachmentArray splitAttach( Attachment const & attach ) { if ( !attach.source.empty() ) { AttachmentArray result; for ( auto & source : attach.source ) { AttachmentArray splitSource = splitAttach( *source.attach ); result.insert( result.end(), splitSource.begin(), splitSource.end() ); } return result; } if ( attach.isImage() ) return splitImage( attach ); return splitBuffer( attach ); } template< typename UIntT > static bool isInRange( UIntT value , UIntT offset, UIntT count ) { return value >= offset && value < offset + count; } template< typename UIntT > static bool areIntersecting( UIntT lhsOffset, UIntT lhsCount , UIntT rhsOffset, UIntT rhsCount ) { return isInRange( lhsOffset, rhsOffset, rhsCount ) || isInRange( rhsOffset, lhsOffset, lhsCount ); } static bool areIntersecting( ImageSubresourceRange const & lhs , ImageSubresourceRange const & rhs ) { return areIntersecting( lhs.baseMipLevel, lhs.levelCount, rhs.baseMipLevel, rhs.levelCount ) && areIntersecting( lhs.baseArrayLayer, lhs.layerCount, rhs.baseArrayLayer, lhs.layerCount ); } static bool areIntersecting( BufferSubresourceRange const & lhs , BufferSubresourceRange const & rhs ) { return areIntersecting( lhs.offset, lhs.size , rhs.offset, rhs.size ); } static bool areOverlapping( ImageViewData const & lhs , ImageViewData const & rhs ) { return lhs.image == rhs.image && areIntersecting( getVirtualRange( lhs.image, lhs.info.viewType, lhs.info.subresourceRange ) , getVirtualRange( rhs.image, rhs.info.viewType, rhs.info.subresourceRange ) ); } static bool areOverlapping( BufferViewData const & lhs , BufferViewData const & rhs ) { return lhs.buffer == rhs.buffer && areIntersecting( lhs.info.subresourceRange , rhs.info.subresourceRange ); } static bool areOverlapping( Attachment const & lhs , Attachment const & rhs ) { if ( lhs.isImage() ) return areOverlapping( *lhs.view().data, *rhs.view().data ); return areOverlapping( *lhs.buffer().data, *rhs.buffer().data ); } void traverseAttachmentPasses( GraphNode & parent , Attachment const * currentAttach , Attachment const * parentAttach , AttachmentTransitions & transitions , GraphNodePtrArray & nodes ); static void traversePassAttach( FramePassNode & node, std::map< uint32_t, Attachment const * > const & attachments , AttachmentTransitions & transitions , GraphNodePtrArray & nodes ) { for ( auto [binding, attach] : attachments ) traverseAttachmentPasses( node , node.getFramePass().getParentAttachment( *attach ), attach , transitions, nodes ); } static void traversePassAttach( FramePassNode & node, std::map< uint32_t, FramePass::SampledAttachment > const & attachments , AttachmentTransitions & transitions , GraphNodePtrArray & nodes ) { for ( auto const & [binding, attach] : attachments ) traverseAttachmentPasses( node , node.getFramePass().getParentAttachment( *attach.attach ), attach.attach , transitions, nodes ); } static void traversePassAttach( FramePassNode & node, AttachmentArray const & targets, Attachment::Flag flag , AttachmentTransitions & transitions , GraphNodePtrArray & nodes ) { for ( auto attach : targets ) { if ( attach->hasFlag( flag ) ) traverseAttachmentPasses( node , node.getFramePass().getParentAttachment( *attach ), attach , transitions, nodes ); } } static void insertTransition( bool isImage , Attachment const * output , Attachment const * input , AttachmentTransitions & transitions ) { if ( isImage ) { if ( output && input ) transitions.imageTransitions.emplace_back( output->view(), *output, *input ); else if ( output ) transitions.imageTransitions.emplace_back( output->view(), *output, Attachment::createDefault( output->view() ) ); else if ( input ) transitions.imageTransitions.emplace_back( input->view(), Attachment::createDefault( input->view() ), *input ); } else { if ( output && input ) transitions.bufferTransitions.emplace_back( output->buffer(), *output, *input ); else if ( output ) transitions.bufferTransitions.emplace_back( output->buffer(), *output, Attachment::createDefault( output->buffer() ) ); else if ( input ) transitions.bufferTransitions.emplace_back( input->buffer(), Attachment::createDefault( input->buffer() ), *input ); } } void traverseAttachmentPasses( GraphNode & parent , Attachment const * outputAttach , Attachment const * inputAttach , AttachmentTransitions & parentTransitions , GraphNodePtrArray & nodes ) { if ( outputAttach ) { for ( auto pass : listAttachmentPasses( *outputAttach ) ) { auto it = std::find_if( nodes.begin(), nodes.end() , [&pass]( GraphNodePtr const & lookup ) { return lookup->getName() == pass->getGroupName(); } ); if ( nodes.end() == it ) { auto & node = static_cast< FramePassNode & >( *nodes.emplace_back( std::make_unique< FramePassNode >( *pass ) ) ); AttachmentTransitions transitions; traversePassAttach( node, pass->getTargets(), Attachment::Flag::InOut, transitions, nodes ); traversePassAttach( node, pass->getInouts(), transitions, nodes ); traversePassAttach( node, pass->getUniforms(), transitions, nodes ); traversePassAttach( node, pass->getSampled(), transitions, nodes ); traversePassAttach( node, pass->getInputs(), transitions, nodes ); traversePassAttach( node, pass->getTargets(), Attachment::Flag::Input, transitions, nodes ); node.setTransitions( mergeIdenticalTransitions( std::move( transitions ) ) ); parent.attachNode( node ); } else { parent.attachNode( **it ); } } } if ( outputAttach && inputAttach ) { auto outputs = splitAttach( *outputAttach ); auto inputs = splitAttach( *inputAttach ); for ( auto output : outputs ) { for ( auto input : inputs ) { if ( areOverlapping( *output, *input ) ) insertTransition( output->isImage(), output, input, parentTransitions ); } } } else if ( outputAttach ) { // Attach to external auto outputs = splitAttach( *outputAttach ); for ( auto output : outputs ) insertTransition( output->isImage(), output, nullptr, parentTransitions ); } else if ( inputAttach ) { // External resource auto inputs = splitAttach( *inputAttach ); for ( auto input : inputs ) insertTransition( input->isImage(), nullptr, input, parentTransitions ); } } static bool hasInPredecessors( GraphNode & parent, GraphNode & child ) { return parent.getPredecessors().end() != std::find( parent.getPredecessors().begin() , parent.getPredecessors().end() , &child ); } static void removeShortcuts( GraphNode & node ) { GraphAdjacentNodeArray & predecessors = node.getPredecessors(); auto it = predecessors.begin(); while ( it != predecessors.end() ) { auto curr = *it; bool found = false; auto oit = predecessors.begin(); while ( oit != predecessors.end() && !found ) { if ( oit != it ) found = hasInPredecessors( **oit, *curr ); ++oit; } removeShortcuts( *curr ); if ( found ) it = predecessors.erase( it ); else ++it; } } static void sortNodes( GraphNode & node , GraphNodePtrArray & sourceGraph , GraphNodePtrArray & targetGraph ) { for ( auto & pred : node.getPredecessors() ) sortNodes( *pred, sourceGraph, targetGraph ); auto it = std::find_if( sourceGraph.begin(), sourceGraph.end() , [&node]( GraphNodePtr const & lookup ) { return &node == lookup.get(); } ); if ( sourceGraph.end() != it ) { targetGraph.emplace_back( std::move( *it ) ); sourceGraph.erase( it ); } } static void sortNodes( RootNode & root , GraphNodePtrArray & graph ) { auto sourceGraph = std::move( graph ); graph.clear(); for ( auto & pred : root.getPredecessors() ) sortNodes( *pred, sourceGraph, graph ); } static void updateState( Attachment const & inputAttach , std::vector< ImageLayout > & states , bool separateDepthStencilLayouts ) { auto id = inputAttach.view().id; while ( states.size() <= id ) states.resize( std::max< size_t >( 1u, states.size() * 2u ) ); states[id] = inputAttach.getImageLayout( separateDepthStencilLayouts ); } static void updateState( Attachment const & inputAttach , std::vector< AccessFlags > & states ) { auto id = inputAttach.buffer().id; while ( states.size() <= id ) states.resize( std::max< size_t >( 1u, states.size() * 2u ) ); states[id] = inputAttach.getAccessMask(); } static void insertNeededTransition( Attachment const & output , Attachment const & input , AttachmentTransitions & transitions , graph::AttachmentStates & states ) { if ( output.isImage() ) { transitions.imageTransitions.emplace_back( output.view(), output, input ); updateState( input, states.imageStates, states.separateDepthStencilLayouts ); } else { transitions.bufferTransitions.emplace_back( output.buffer(), output, input ); updateState( input, states.bufferStates ); } } static bool isInNeededState( Attachment const & inputAttach , std::vector< ImageLayout > const & states , bool separateDepthStencilLayouts ) { auto id = inputAttach.view().id; if ( states.size() <= id ) return false; auto & state = states[id]; return state == inputAttach.getImageLayout( separateDepthStencilLayouts ); } static bool isInNeededState( Attachment const & inputAttach , std::vector< AccessFlags > const & states ) { auto id = inputAttach.buffer().id; if ( states.size() <= id ) return false; auto & state = states[id]; return state == inputAttach.getAccessMask(); } static bool isInNeededState( Attachment const & inputAttach , AttachmentStates const & states ) { if ( inputAttach.isImage() ) return isInNeededState( inputAttach, states.imageStates, states.separateDepthStencilLayouts ); return isInNeededState( inputAttach, states.bufferStates ); } static void buildTransitions( GraphNodePtrArray const & graph , graph::AttachmentStates & states ) { for ( auto & node : graph ) { if ( node->getKind() == GraphNode::Kind::FramePass ) { AttachmentTransitions transitions; for ( auto & transition : node->getImageTransitions() ) { if ( !isInNeededState( transition.inputAttach, states ) ) insertNeededTransition( transition.outputAttach, transition.inputAttach, transitions, states ); } for ( auto & transition : node->getBufferTransitions() ) { if ( !isInNeededState( transition.inputAttach, states ) ) insertNeededTransition( transition.outputAttach, transition.inputAttach, transitions, states ); } node->setTransitions( std::move( transitions ) ); } } AttachmentTransitions transitions; } } //********************************************************************************************* AttachmentArray findEndPoints( FramePassArray const & passes ) { // List all attaches AttachmentArray allAttachs; endpoints::listAllAttachs( passes, allAttachs ); // Filter out the attaches that are used as parent to pass attaches AttachmentArray result; endpoints::listNonParentAttachs( passes, allAttachs, result ); // Also look for passes without outputs, and add their inputs endpoints::addSinkPassInputs( passes, result ); // Remove attachs for which the pass has other output attachs which are not in the list endpoints::removeMismatchs( result ); return result; } //********************************************************************************************* void buildGraph( AttachmentArray const & endPoints , RootNode & root , GraphNodePtrArray & graph , bool separateDepthStencilLayouts ) { // First generate the graph with all transitions and links. AttachmentTransitions transitions; for ( auto endPoint : endPoints ) graph::traverseAttachmentPasses( root, endPoint, nullptr, transitions, graph ); // Then remove the shortcuts (if pass C depends on A and B, if B depends on A, then remove link between A and C) graph::removeShortcuts( root ); // Now sort the graph nodes regarding their position in the final graph. graph::sortNodes( root, graph ); // Eventually parse the sorted nodes to generate a curated transitions list per node. graph::AttachmentStates states{ separateDepthStencilLayouts }; graph::buildTransitions( graph, states ); } //********************************************************************************************* } ================================================ FILE: source/RenderGraph/GraphBuilder.hpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "BuilderCommon.hpp" namespace crg::builder { AttachmentArray findEndPoints( FramePassArray const & passes ); void buildGraph( AttachmentArray const & endPoints , RootNode & root , GraphNodePtrArray & graph , bool separateDepthStencilLayouts ); } ================================================ FILE: source/RenderGraph/GraphContext.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/Exception.hpp" #include "RenderGraph/Log.hpp" #include #include #include #pragma warning( push ) #pragma warning( disable: 5262 ) #include #include #pragma warning( pop ) namespace crg { using lock_type = std::unique_lock< std::mutex >; GraphContext::GraphContext( VkDevice device , VkPipelineCache cache , VkAllocationCallbacks const * allocator , VkPhysicalDeviceMemoryProperties memoryProperties , VkPhysicalDeviceProperties properties , bool separateDepthStencilLayouts , PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr ) : device{ device } , cache{ cache } , allocator{ allocator } , memoryProperties{ std::move( memoryProperties ) } , properties{ std::move( properties ) } , separateDepthStencilLayouts{ separateDepthStencilLayouts } { #pragma warning( push ) #pragma warning( disable: 4191 ) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-function-type-strict" #define DECL_vkFunction( name )\ if ( vkGetDeviceProcAddr && device )\ vk##name = reinterpret_cast< PFN_vk##name >( vkGetDeviceProcAddr( device, "vk"#name ) ) DECL_vkFunction( CreateGraphicsPipelines ); DECL_vkFunction( CreateComputePipelines ); DECL_vkFunction( DestroyPipeline ); DECL_vkFunction( CreatePipelineLayout ); DECL_vkFunction( DestroyPipelineLayout ); DECL_vkFunction( CreateDescriptorSetLayout ); DECL_vkFunction( DestroyDescriptorSetLayout ); DECL_vkFunction( CreateDescriptorPool ); DECL_vkFunction( DestroyDescriptorPool ); DECL_vkFunction( AllocateDescriptorSets ); DECL_vkFunction( FreeDescriptorSets ); DECL_vkFunction( CreateBuffer ); DECL_vkFunction( DestroyBuffer ); DECL_vkFunction( CreateBufferView ); DECL_vkFunction( DestroyBufferView ); DECL_vkFunction( GetBufferMemoryRequirements ); DECL_vkFunction( GetImageMemoryRequirements ); DECL_vkFunction( AllocateMemory ); DECL_vkFunction( FreeMemory ); DECL_vkFunction( BindBufferMemory ); DECL_vkFunction( BindImageMemory ); DECL_vkFunction( MapMemory ); DECL_vkFunction( UnmapMemory ); DECL_vkFunction( FlushMappedMemoryRanges ); DECL_vkFunction( InvalidateMappedMemoryRanges ); DECL_vkFunction( CreateRenderPass ); DECL_vkFunction( DestroyRenderPass ); DECL_vkFunction( CreateFramebuffer ); DECL_vkFunction( DestroyFramebuffer ); DECL_vkFunction( CreateImage ); DECL_vkFunction( DestroyImage ); DECL_vkFunction( CreateImageView ); DECL_vkFunction( DestroyImageView ); DECL_vkFunction( CreateSampler ); DECL_vkFunction( DestroySampler ); DECL_vkFunction( CreateCommandPool ); DECL_vkFunction( DestroyCommandPool ); DECL_vkFunction( AllocateCommandBuffers ); DECL_vkFunction( FreeCommandBuffers ); DECL_vkFunction( CreateSemaphore ); DECL_vkFunction( DestroySemaphore ); DECL_vkFunction( UpdateDescriptorSets ); DECL_vkFunction( BeginCommandBuffer ); DECL_vkFunction( EndCommandBuffer ); DECL_vkFunction( QueueSubmit ); DECL_vkFunction( CreateQueryPool ); DECL_vkFunction( DestroyQueryPool ); DECL_vkFunction( GetQueryPoolResults ); DECL_vkFunction( ResetCommandBuffer ); DECL_vkFunction( CreateEvent ); DECL_vkFunction( DestroyEvent ); DECL_vkFunction( ResetEvent ); DECL_vkFunction( SetEvent ); DECL_vkFunction( GetEventStatus ); DECL_vkFunction( CreateFence ); DECL_vkFunction( DestroyFence ); DECL_vkFunction( GetFenceStatus ); DECL_vkFunction( WaitForFences ); DECL_vkFunction( ResetFences ); DECL_vkFunction( CmdBindPipeline ); DECL_vkFunction( CmdBindDescriptorSets ); DECL_vkFunction( CmdBindVertexBuffers ); DECL_vkFunction( CmdBindIndexBuffer ); DECL_vkFunction( CmdClearColorImage ); DECL_vkFunction( CmdClearDepthStencilImage ); DECL_vkFunction( CmdDispatch ); DECL_vkFunction( CmdDispatchIndirect ); DECL_vkFunction( CmdDraw ); DECL_vkFunction( CmdDrawIndexed ); DECL_vkFunction( CmdDrawIndexedIndirect ); DECL_vkFunction( CmdDrawIndirect ); DECL_vkFunction( CmdBeginRenderPass ); DECL_vkFunction( CmdEndRenderPass ); DECL_vkFunction( CmdPushConstants ); DECL_vkFunction( CmdResetQueryPool ); DECL_vkFunction( CmdWriteTimestamp ); DECL_vkFunction( CmdPipelineBarrier ); DECL_vkFunction( CmdBlitImage ); DECL_vkFunction( CmdCopyBuffer ); DECL_vkFunction( CmdCopyBufferToImage ); DECL_vkFunction( CmdCopyImage ); DECL_vkFunction( CmdCopyImageToBuffer ); DECL_vkFunction( CmdExecuteCommands ); DECL_vkFunction( CmdResetEvent ); DECL_vkFunction( CmdSetEvent ); DECL_vkFunction( CmdWaitEvents ); DECL_vkFunction( CmdFillBuffer ); #if VK_EXT_debug_utils DECL_vkFunction( SetDebugUtilsObjectNameEXT ); DECL_vkFunction( CmdBeginDebugUtilsLabelEXT ); DECL_vkFunction( CmdEndDebugUtilsLabelEXT ); #endif #if VK_EXT_debug_marker DECL_vkFunction( DebugMarkerSetObjectNameEXT ); DECL_vkFunction( CmdDebugMarkerBeginEXT ); DECL_vkFunction( CmdDebugMarkerEndEXT ); #endif #undef DECL_vkFunction #pragma clang diagnostic pop #pragma warning( pop ) } GraphContext::~GraphContext()noexcept { #if VK_EXT_debug_utils || VK_EXT_debug_marker for ( auto const & [_, alloc] : m_allocated ) { std::stringstream stream; stream << "Leaked [" << alloc.type << "](" << alloc.name << "), allocation stack:\n" << alloc.callstack; Logger::logError( stream.str() ); } #endif } #if VK_EXT_debug_utils || VK_EXT_debug_marker void GraphContext::vkCmdBeginDebugBlock( VkCommandBuffer commandBuffer , DebugBlockInfo const & labelInfo )const { #if VK_EXT_debug_utils doBeginDebugUtilsLabel( commandBuffer , { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT , nullptr , labelInfo.markerName.c_str() , { labelInfo.colour[0] , labelInfo.colour[1] , labelInfo.colour[2] , labelInfo.colour[3] } } ); #endif #if VK_EXT_debug_marker doDebugMarkerBegin( commandBuffer , { VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT , nullptr , labelInfo.markerName.c_str() , { labelInfo.colour[0] , labelInfo.colour[1] , labelInfo.colour[2] , labelInfo.colour[3] } } ); #endif } void GraphContext::vkCmdEndDebugBlock( VkCommandBuffer commandBuffer )const { #if VK_EXT_debug_utils doEndDebugUtilsLabel( commandBuffer ); #endif #if VK_EXT_debug_marker doDebugMarkerEnd( commandBuffer ); #endif } #endif std::array< float, 4u > GraphContext::getNextRainbowColour()const { static float currentColourHue{ 0.0f }; currentColourHue += 0.0125f; if ( currentColourHue > 1.0f ) { currentColourHue = 0.0f; } float brightness = 1.0f; float saturation = 1.0f; float h = currentColourHue == 1.0f ? 0 : currentColourHue * 6.0f; float f = h - std::floor( h ); float p = brightness * ( 1.0f - saturation ); float q = brightness * ( 1.0f - saturation * f ); float t = brightness * ( 1.0f - ( saturation * ( 1.0f - f ) ) ); if ( h < 1 ) { return { brightness, t, p, 1.0f }; } if ( h < 2 ) { return { q, brightness, p, 1.0f }; } if ( h < 3 ) { return { p, brightness, t, 1.0f }; } if ( h < 4 ) { return { p, q, brightness, 1.0f }; } if ( h < 5 ) { return { t, p, brightness, 1.0f }; } return { brightness, p, q, 1.0f }; } uint32_t GraphContext::deduceMemoryType( uint32_t typeBits , VkMemoryPropertyFlags requirements )const { for ( uint32_t i = 0; i < memoryProperties.memoryTypeCount; ++i ) { if ( ( typeBits & 1 ) == 1 && ( memoryProperties.memoryTypes[i].propertyFlags & requirements ) == requirements ) { return i; } typeBits >>= 1; } Logger::logError( "Could not deduce memory type" ); CRG_Exception( "Could not deduce memory type" ); } #if VK_EXT_debug_utils void GraphContext::doBeginDebugUtilsLabel( VkCommandBuffer commandBuffer , VkDebugUtilsLabelEXT const & labelInfo )const { if ( vkCmdBeginDebugUtilsLabelEXT ) { vkCmdBeginDebugUtilsLabelEXT( commandBuffer, &labelInfo ); } } void GraphContext::doEndDebugUtilsLabel( VkCommandBuffer commandBuffer )const { if ( vkCmdEndDebugUtilsLabelEXT ) { vkCmdEndDebugUtilsLabelEXT( commandBuffer ); } } #endif #if VK_EXT_debug_marker void GraphContext::doDebugMarkerBegin( VkCommandBuffer commandBuffer , VkDebugMarkerMarkerInfoEXT const & labelInfo )const { if ( vkCmdDebugMarkerBeginEXT ) { vkCmdDebugMarkerBeginEXT( commandBuffer, &labelInfo ); } } void GraphContext::doDebugMarkerEnd( VkCommandBuffer commandBuffer )const { if ( vkCmdDebugMarkerEndEXT ) { vkCmdDebugMarkerEndEXT( commandBuffer ); } } #endif #if VK_EXT_debug_utils || VK_EXT_debug_marker void GraphContext::doRegisterObject( uint64_t object , uint32_t objectType , std::string const & objectName , std::string const & typeName ) { doRegisterObjectName( object , objectType , objectName ); std::stringstream stream; stream.imbue( std::locale{ "C" } ); stream << "Created " << typeName << " [0x" << std::hex << std::setw( 8u ) << std::setfill( '0' ) << object << "]" << " - " << objectName; Logger::logTrace( stream.str() ); std::stringstream callStack; if ( m_callstackCallback ) { callStack << m_callstackCallback(); } lock_type lock{ m_mutex }; m_allocated.insert_or_assign( object , ObjectAllocation{ typeName, objectName, callStack.str() } ); } void GraphContext::doRegisterObjectName( uint64_t object , uint32_t objectType , std::string const & objectName ) { # if VK_EXT_debug_utils if ( vkSetDebugUtilsObjectNameEXT ) { VkDebugUtilsObjectNameInfoEXT nameInfo{ VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT , nullptr , VkObjectType( objectType ) , object , objectName.c_str() }; vkSetDebugUtilsObjectNameEXT( device, &nameInfo ); } # endif # if VK_EXT_debug_marker if ( vkDebugMarkerSetObjectNameEXT ) { VkDebugMarkerObjectNameInfoEXT nameInfo{ VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT , nullptr , VkDebugReportObjectTypeEXT( objectType ) , object , objectName.c_str() }; vkDebugMarkerSetObjectNameEXT( device, &nameInfo ); } # endif } void GraphContext::doUnregisterObject( uint64_t object ) { if ( object ) { lock_type lock{ m_mutex }; if ( auto it = m_allocated.find( size_t( object ) ); it != m_allocated.end() ) { std::stringstream stream; stream.imbue( std::locale{ "C" } ); stream << "Destroyed " << it->second.type << " [0x" << std::hex << std::setw( 8u ) << std::setfill( '0' ) << object << "]" << " - " << it->second.name; Logger::logTrace( stream.str() ); m_allocated.erase( it ); } else { std::stringstream stream; stream.imbue( std::locale{ "C" } ); stream << "Destroyed Unknown" << " [0x" << std::hex << std::setw( 8u ) << std::setfill( '0' ) << object << "]" << " - Unregistered"; Logger::logTrace( stream.str() ); } } } #endif void checkVkResult( VkResult result, char const * const stepName ) { if ( result != VK_SUCCESS ) { Logger::logError( stepName ); CRG_Exception( stepName ); } } void checkVkResult( VkResult result, std::string const & stepName ) { checkVkResult( result, stepName.c_str() ); } } ================================================ FILE: source/RenderGraph/GraphNode.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/GraphNode.hpp" #include "RenderGraph/GraphVisitor.hpp" #include "RenderGraph/FrameGraph.hpp" #include "RenderGraph/FramePass.hpp" namespace crg { //********************************************************************************************* GraphNode::~GraphNode()noexcept = default; GraphNode::GraphNode( GraphNode && rhs )noexcept : kind{ rhs.kind } , id{ rhs.id } , name{ std::move( rhs.name ) } , group{ rhs.group } , prev{ std::move( rhs.prev ) } { rhs.kind = Kind::Undefined; rhs.id = 0u; } GraphNode::GraphNode( Kind pkind , uint32_t pid , std::string pname , FramePassGroup const & passGroup ) : kind{ pkind } , id{ pid } , name{ std::move( pname ) } , group{ &passGroup } { } void GraphNode::attachNode( GraphNode & child ) { if ( prev.end() == std::find( prev.begin(), prev.end(), &child ) ) prev.push_back( &child ); } //********************************************************************************************* FramePassNode::~FramePassNode()noexcept = default; FramePassNode::FramePassNode( FramePass const & framePass ) : GraphNode{ MyKind, framePass.getId(), framePass.getGroupName(), framePass.getGroup() } , pass{ &framePass } { } void FramePassNode::accept( GraphVisitor * vis )const { vis->visitFramePassNode( this ); } //********************************************************************************************* RootNode::~RootNode()noexcept = default; RootNode::RootNode( FrameGraph const & frameGraph ) : GraphNode{ MyKind, 0u, frameGraph.getName(), frameGraph.getDefaultGroup() } , graph{ &frameGraph } { } void RootNode::accept( GraphVisitor * vis )const { vis->visitRootNode( this ); } //********************************************************************************************* FramePass const * getFramePass( GraphNode const & node ) { if ( !isFramePassNode( node ) ) { return nullptr; } return &nodeCast< FramePassNode >( node ).getFramePass(); } //********************************************************************************************* } ================================================ FILE: source/RenderGraph/LayerLayoutStatesHandler.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/LayerLayoutStatesHandler.hpp" #include "RenderGraph/ImageData.hpp" #include "RenderGraph/ImageViewData.hpp" #include namespace crg { LayerLayoutStatesHandler::LayerLayoutStatesHandler( LayerLayoutStatesMap const & rhs ) : images{ rhs } { } void LayerLayoutStatesHandler::addStates( LayerLayoutStatesHandler const & data ) { for ( auto & state : data.images ) { images.insert( state ); } } void LayerLayoutStatesHandler::setLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange , LayoutState const & layoutState ) { auto range = getVirtualRange( image , viewType , subresourceRange ); auto [it, _] = images.try_emplace( image.id ); addSubresourceRangeLayout( it->second , range , layoutState ); } void LayerLayoutStatesHandler::setLayoutState( crg::ImageViewId view , LayoutState const & layoutState ) { assert( view.data->source.empty() && "Merged image views must be resolved before setting their layout state" ); setLayoutState( view.data->image , view.data->info.viewType , view.data->info.subresourceRange , layoutState ); } LayoutState LayerLayoutStatesHandler::getLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange )const { if ( auto imageIt = images.find( image.id ); imageIt != images.end() ) { auto range = getVirtualRange( image , viewType , subresourceRange ); return getSubresourceRangeLayout( imageIt->second , range ); } return { ImageLayout::eUndefined, { AccessFlags::eNone, PipelineStageFlags::eBottomOfPipe } }; } LayoutState LayerLayoutStatesHandler::getLayoutState( ImageViewId view )const { assert( view.data->source.empty() && "Merged image views must be resolved before finding their layout state" ); return getLayoutState( view.data->image , view.data->info.viewType , view.data->info.subresourceRange ); } } ================================================ FILE: source/RenderGraph/Log.cpp ================================================ #include "RenderGraph/Log.hpp" #pragma warning( push ) #pragma warning( disable: 5262 ) #include #include #include #pragma warning( pop ) namespace crg { namespace log { using lock_type = std::unique_lock< std::mutex >; static void doLog( std::string_view message , bool newLine , FILE * stream )noexcept { fprintf( stream, "%s", message.data() ); if ( newLine ) { fprintf( stream, "\n" ); } } } Logger::Logger() : m_trace{ []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stdout ); } } , m_debug{ []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stdout ); } } , m_info{ []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stdout ); } } , m_warning{ []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stdout ); } } , m_error{ []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stderr ); } } { } void Logger::logTrace( std::string_view message, bool newLine )noexcept { doGetInstance().m_trace( message, newLine ); } void Logger::logDebug( std::string_view message, bool newLine )noexcept { doGetInstance().m_debug( message, newLine ); } void Logger::logInfo( std::string_view message, bool newLine )noexcept { doGetInstance().m_info( message, newLine ); } void Logger::logWarning( std::string_view message, bool newLine )noexcept { doGetInstance().m_warning( message, newLine ); } void Logger::logError( std::string_view message, bool newLine )noexcept { doGetInstance().m_error( message, newLine ); } void Logger::setTraceCallback( LogCallback callback ) { if ( callback ) doGetInstance().m_trace = std::move( callback ); else doGetInstance().m_trace = []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stdout ); }; } void Logger::setDebugCallback( LogCallback callback ) { if ( callback ) doGetInstance().m_debug = std::move( callback ); else doGetInstance().m_debug = []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stdout ); }; } void Logger::setInfoCallback( LogCallback callback ) { if ( callback ) doGetInstance().m_info = std::move( callback ); else doGetInstance().m_info = []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stdout ); }; } void Logger::setWarningCallback( LogCallback callback ) { if ( callback ) doGetInstance().m_warning = std::move( callback ); else doGetInstance().m_warning = []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stdout ); }; } void Logger::setErrorCallback( LogCallback callback ) { if ( callback ) doGetInstance().m_error = std::move( callback ); else doGetInstance().m_error = []( std::string_view msg, bool newLine )noexcept { log::doLog( msg, newLine, stderr ); }; } Logger & Logger::doGetInstance()noexcept { static Logger instance; static std::mutex mutex; log::lock_type lock{ mutex }; return instance; } } ================================================ FILE: source/RenderGraph/RecordContext.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/RecordContext.hpp" #include "RenderGraph/Exception.hpp" #include "RenderGraph/Log.hpp" #include "RenderGraph/ResourceHandler.hpp" #include "RenderGraph/RunnableGraph.hpp" #include #include #include #include #pragma warning( push ) #pragma warning( disable: 5262 ) #include #pragma warning( pop ) namespace crg { //************************************************************************************************ namespace recctx { static ImageSubresourceRange adaptRange( GraphContext const & context , ImageType type , PixelFormat format , ImageSubresourceRange const & subresourceRange ) { ImageSubresourceRange result = subresourceRange; if ( type == ImageType::e3D ) { result.baseArrayLayer = 0; result.layerCount = 1; } if ( !context.separateDepthStencilLayouts && isDepthStencilFormat( format ) && ( checkFlag( result.aspectMask, ImageAspectFlags::eDepth ) || checkFlag( result.aspectMask, ImageAspectFlags::eStencil ) ) ) result.aspectMask = ImageAspectFlags::eDepthStencil; return result; } } //************************************************************************************************ RecordContext::RecordContext( ContextResourcesCache & resources ) : m_handler{ &resources.getHandler() } , m_resources{ &resources } , m_prevPipelineState{ AccessFlags::eNone, PipelineStageFlags::eBottomOfPipe } , m_currPipelineState{ AccessFlags::eNone, PipelineStageFlags::eBottomOfPipe } , m_nextPipelineState{ AccessFlags::eNone, PipelineStageFlags::eBottomOfPipe } { } RecordContext::RecordContext( ResourceHandler & handler ) : m_handler{ &handler } , m_resources{ nullptr } { } void RecordContext::addStates( RecordContext const & data ) { m_images.addStates( data.m_images ); for ( auto & state : data.m_buffers ) { m_buffers.insert( state ); } if ( m_prevPipelineState.access < data.m_currPipelineState.access ) { m_prevPipelineState = data.m_currPipelineState; } } void RecordContext::setNextPipelineState( PipelineState const & state , LayerLayoutStatesMap const & imageLayouts ) { m_prevPipelineState = m_currPipelineState; m_currPipelineState = m_nextPipelineState; m_nextPipelineState = state; m_nextImages = LayerLayoutStatesHandler{ imageLayouts }; } void RecordContext::setLayoutState( ImageViewId view , LayoutState const & layoutState ) { m_images.setLayoutState( view, layoutState ); } LayoutState RecordContext::getLayoutState( ImageViewId view )const { return m_images.getLayoutState( view ); } void RecordContext::setLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange , LayoutState const & layoutState ) { m_images.setLayoutState( image , viewType , subresourceRange , layoutState ); } LayoutState RecordContext::getLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange )const { return m_images.getLayoutState( image , viewType , subresourceRange ); } LayoutState RecordContext::getNextLayoutState( ImageViewId view )const { return m_nextImages.getLayoutState( view ); } LayoutState RecordContext::getNextLayoutState( ImageId image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange )const { return m_nextImages.getLayoutState( image , viewType , subresourceRange ); } void RecordContext::registerImplicitTransition( RunnablePass const & pass , ImageViewId view , RecordContext::ImplicitAction action ) { registerImplicitTransition( { &pass, view, std::move( action ) } ); } void RecordContext::registerImplicitTransition( RunnablePass const & pass , BufferViewId view , RecordContext::ImplicitAction action ) { registerImplicitTransition( { &pass, view, std::move( action ) } ); } void RecordContext::registerImplicitTransition( ImplicitImageTransition transition ) { m_implicitImageTransitions.emplace_back( std::move( transition ) ); } void RecordContext::registerImplicitTransition( ImplicitBufferTransition transition ) { m_implicitBufferTransitions.emplace_back( std::move( transition ) ); } void RecordContext::runImplicitTransition( VkCommandBuffer commandBuffer , uint32_t index , ImageViewId view ) { auto it = std::find_if( m_implicitImageTransitions.begin() , m_implicitImageTransitions.end() , [&view]( ImplicitImageTransition const & lookup ) { return lookup.view == view; } ); if ( it != m_implicitImageTransitions.end() ) { auto pass = it->pass; auto action = it->action; m_implicitImageTransitions.erase( it ); if ( !pass->isEnabled() ) { action( *this, commandBuffer, index ); } } } void RecordContext::runImplicitTransition( VkCommandBuffer commandBuffer , uint32_t index , BufferViewId view ) { auto it = std::find_if( m_implicitBufferTransitions.begin() , m_implicitBufferTransitions.end() , [&view]( ImplicitBufferTransition const & lookup ) { return lookup.view == view; } ); if ( it != m_implicitBufferTransitions.end() ) { auto pass = it->pass; auto action = it->action; m_implicitBufferTransitions.erase( it ); if ( !pass->isEnabled() ) { action( *this, commandBuffer, index ); } } } void RecordContext::setAccessState( BufferViewId buffer , AccessState const & accessState ) { return setAccessState( buffer.data->buffer, getSubresourceRange( buffer ), accessState ); } AccessState RecordContext::getAccessState( BufferViewId buffer )const { return getAccessState( buffer.data->buffer, getSubresourceRange( buffer ) ); } void RecordContext::setAccessState( BufferId buffer , [[maybe_unused]] BufferSubresourceRange const & subresourceRange , AccessState const & accessState ) { m_buffers.insert_or_assign( buffer.id, accessState ); } AccessState const & RecordContext::getAccessState( BufferId buffer , [[maybe_unused]] BufferSubresourceRange const & subresourceRange )const { if ( auto bufferIt = m_buffers.find( buffer.id ); bufferIt != m_buffers.end() ) { return bufferIt->second; } static AccessState const dummy{ AccessFlags::eNone, PipelineStageFlags::eBottomOfPipe }; return dummy; } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , ImageViewId const & view , ImageLayout initialLayout , LayoutState const & wantedState , bool force ) { memoryBarrier( commandBuffer , view.data->image , view.data->info.viewType , getSubresourceRange( view ) , initialLayout , wantedState , force ); } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , ImageId const & image , ImageSubresourceRange const & subresourceRange , ImageLayout initialLayout , LayoutState const & wantedState , bool force ) { memoryBarrier( commandBuffer , image , ImageViewType( image.data->info.imageType ) , subresourceRange , initialLayout , wantedState , force ); } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , ImageId const & image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange , ImageLayout initialLayout , LayoutState const & wantedState , bool force ) { auto range = recctx::adaptRange( m_resources->getContext() , image.data->info.imageType , image.data->info.format , subresourceRange ); auto from = getLayoutState( image , viewType , range ); if ( from.layout == ImageLayout::eUndefined ) { from = makeLayoutState( initialLayout ); } if ( force || ( ( from.layout != wantedState.layout || from.state.pipelineStage != wantedState.state.pipelineStage ) && wantedState.layout != ImageLayout::eUndefined ) ) { auto & resources = getResources(); VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER , nullptr , getAccessFlags( from.state.access ) , getAccessFlags( wantedState.state.access ) , convert( from.layout ) , convert( wantedState.layout ) , VK_QUEUE_FAMILY_IGNORED , VK_QUEUE_FAMILY_IGNORED , resources.createImage( image ) , convert( range ) }; resources->vkCmdPipelineBarrier( commandBuffer , getPipelineStageFlags( from.state.pipelineStage ) , getPipelineStageFlags( wantedState.state.pipelineStage ) , VK_DEPENDENCY_BY_REGION_BIT , 0u , nullptr , 0u , nullptr , 1u , &barrier ); setLayoutState( image , viewType , range , wantedState ); } } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , ImageViewId const & view , LayoutState const & wantedState , bool force ) { memoryBarrier( commandBuffer , view.data->image , view.data->info.viewType , getSubresourceRange( view ) , wantedState , force ); } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , ImageId const & image , ImageSubresourceRange const & subresourceRange , LayoutState const & wantedState , bool force ) { memoryBarrier( commandBuffer , image , ImageViewType( image.data->info.imageType ) , subresourceRange , wantedState , force ); } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , ImageId const & image , ImageViewType viewType , ImageSubresourceRange const & subresourceRange , LayoutState const & wantedState , bool force ) { memoryBarrier( commandBuffer , image , viewType , subresourceRange , ImageLayout::eUndefined , wantedState , force ); } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , BufferViewId view , AccessState const & initialState , AccessState const & wantedState , bool force ) { memoryBarrier( commandBuffer , view.data->buffer , getSubresourceRange( view ) , initialState , wantedState , force ); } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , BufferId buffer , BufferSubresourceRange const & subresourceRange , AccessState const & initialState , AccessState const & wantedState , bool force ) { auto from = getAccessState( buffer , subresourceRange ); if ( checkFlag( from.pipelineStage, PipelineStageFlags::eBottomOfPipe ) ) { from = initialState; } if ( force || ( from.access != wantedState.access || from.pipelineStage != wantedState.pipelineStage ) ) { auto & resources = getResources(); VkBufferMemoryBarrier barrier{ VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER , nullptr , getAccessFlags( from.access ) , getAccessFlags( wantedState.access ) , VK_QUEUE_FAMILY_IGNORED , VK_QUEUE_FAMILY_IGNORED , resources.createBuffer( buffer ) , subresourceRange.offset , subresourceRange.size }; resources->vkCmdPipelineBarrier( commandBuffer , getPipelineStageFlags( from.pipelineStage ) , getPipelineStageFlags( wantedState.pipelineStage ) , VK_DEPENDENCY_BY_REGION_BIT , 0u , nullptr , 1u , &barrier , 0u , nullptr ); setAccessState( buffer , subresourceRange , wantedState ); } } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , BufferId buffer , BufferSubresourceRange const & subresourceRange , AccessState const & wantedState , bool force ) { memoryBarrier( commandBuffer , buffer , subresourceRange , { AccessFlags::eNone, PipelineStageFlags::eBottomOfPipe } , wantedState , force ); } void RecordContext::memoryBarrier( VkCommandBuffer commandBuffer , BufferViewId view , AccessState const & wantedState , bool force ) { memoryBarrier( commandBuffer , view.data->buffer , getSubresourceRange( view ) , wantedState , force ); } GraphContext & RecordContext::getContext()const { return getResources().getContext(); } ContextResourcesCache & RecordContext::getResources()const { if ( !m_resources ) { Logger::logError( "No resources available." ); CRG_Exception( "No resources available." ); } return *m_resources; } void RecordContext::copyImage( VkCommandBuffer commandBuffer , uint32_t index , ImageViewId srcView , ImageViewId dstView , Extent2D const & extent , ImageLayout finalLayout ) { runImplicitTransition( commandBuffer, index, srcView ); auto & srcSubresource = getSubresourceRange( srcView ); auto & dstSubresource = getSubresourceRange( dstView ); VkImageCopy region{ getSubresourceLayer( srcSubresource ), VkOffset3D{ 0u, 0u, 0u } , getSubresourceLayer( dstSubresource ), VkOffset3D{ 0u, 0u, 0u } , { extent.width, extent.height, 1u } }; memoryBarrier( commandBuffer, srcView, makeLayoutState( ImageLayout::eTransferSrc ) ); memoryBarrier( commandBuffer, dstView, makeLayoutState( ImageLayout::eTransferDst ) ); auto & resources = getResources(); resources->vkCmdCopyImage( commandBuffer , resources.createImage( srcView.data->image ), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , resources.createImage( dstView.data->image ), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1u, ®ion ); if ( finalLayout != ImageLayout::eUndefined ) memoryBarrier( commandBuffer, dstView, makeLayoutState( finalLayout ) ); } void RecordContext::blitImage( VkCommandBuffer commandBuffer , uint32_t index , ImageViewId srcView , ImageViewId dstView , Rect2D const & srcRect , Rect2D const & dstRect , FilterMode filter , ImageLayout finalLayout ) { runImplicitTransition( commandBuffer, index, srcView ); auto & srcSubresource = getSubresourceRange( srcView ); auto & dstSubresource = getSubresourceRange( dstView ); memoryBarrier( commandBuffer, srcView, makeLayoutState( ImageLayout::eTransferSrc ) ); memoryBarrier( commandBuffer, dstView, makeLayoutState( ImageLayout::eTransferDst ) ); auto & resources = getResources(); VkImageBlit region{ getSubresourceLayer( srcSubresource ), { VkOffset3D{ srcRect.offset.x, srcRect.offset.y, 0u }, VkOffset3D{ int32_t( srcRect.extent.width ), int32_t( srcRect.extent.height ), 1 } } , getSubresourceLayer( dstSubresource ), { VkOffset3D{ dstRect.offset.x, dstRect.offset.y, 0u }, VkOffset3D{ int32_t( dstRect.extent.width ), int32_t( dstRect.extent.height ), 1 } } }; resources->vkCmdBlitImage( commandBuffer , resources.createImage( srcView.data->image ), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , resources.createImage( dstView.data->image ), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1u, ®ion, convert( filter ) ); if ( finalLayout != ImageLayout::eUndefined ) memoryBarrier( commandBuffer, dstView, makeLayoutState( finalLayout ) ); } void RecordContext::clearAttachment( VkCommandBuffer commandBuffer , ImageViewId dstView , ClearColorValue const & clearValue , ImageLayout finalLayout ) { auto & resources = getResources(); memoryBarrier( commandBuffer, dstView, makeLayoutState( ImageLayout::eTransferDst ) ); auto subresourceRange = convert( getSubresourceRange( dstView ) ); assert( isColourFormat( getFormat( dstView ) ) ); auto vkClearValue = convert( clearValue ); resources->vkCmdClearColorImage( commandBuffer , resources.createImage( dstView.data->image ), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , &vkClearValue, 1u, &subresourceRange ); if ( finalLayout != ImageLayout::eUndefined ) memoryBarrier( commandBuffer, dstView, makeLayoutState( finalLayout ) ); } void RecordContext::clearAttachment( VkCommandBuffer commandBuffer , ImageViewId dstView , ClearDepthStencilValue const & clearValue , ImageLayout finalLayout ) { auto & resources = getResources(); memoryBarrier( commandBuffer, dstView, makeLayoutState( ImageLayout::eTransferDst ) ); auto subresourceRange = convert( getSubresourceRange( dstView ) ); assert( isDepthOrStencilFormat( getFormat( dstView ) ) ); auto vkClearValue = convert( clearValue ); resources->vkCmdClearDepthStencilImage( commandBuffer , resources.createImage( dstView.data->image ), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , &vkClearValue, 1u, &subresourceRange ); if ( finalLayout != ImageLayout::eUndefined ) memoryBarrier( commandBuffer, dstView, makeLayoutState( finalLayout ) ); } void RecordContext::copyBuffer( VkCommandBuffer commandBuffer , uint32_t index , BufferViewId srcView , BufferViewId dstView , DeviceSize srcOffset, DeviceSize dstOffset , DeviceSize size , AccessState const & finalState ) { runImplicitTransition( commandBuffer, index, srcView ); memoryBarrier( commandBuffer, srcView, { AccessFlags::eTransferRead, PipelineStageFlags::eTransfer } ); memoryBarrier( commandBuffer, dstView, { AccessFlags::eTransferWrite, PipelineStageFlags::eTransfer } ); auto & resources = getResources(); VkBufferCopy region{ srcOffset, dstOffset, size }; resources->vkCmdCopyBuffer( commandBuffer , resources.createBuffer( srcView.data->buffer ) , resources.createBuffer( dstView.data->buffer ) , 1u, ®ion ); if ( finalState != AccessState{} ) memoryBarrier( commandBuffer, dstView, finalState ); } void RecordContext::clearBuffer( VkCommandBuffer commandBuffer , BufferViewId dstView , uint32_t clearValue , AccessState const & finalState ) { auto & resources = getResources(); memoryBarrier( commandBuffer, dstView, { AccessFlags::eTransferWrite, PipelineStageFlags::eTransfer } ); auto subresourceRange = getSubresourceRange( dstView ); resources->vkCmdFillBuffer( commandBuffer , resources.createBuffer( dstView.data->buffer ) , subresourceRange.offset, subresourceRange.size , clearValue ); if ( finalState != AccessState{} ) memoryBarrier( commandBuffer, dstView, finalState ); } RecordContext::ImplicitAction RecordContext::copyImage( ImageViewId srcView , ImageViewId dstView , Extent2D const & extent , ImageLayout finalLayout ) { return [srcView, dstView, extent, finalLayout]( RecordContext & recContext , VkCommandBuffer commandBuffer , uint32_t index ) { recContext.copyImage( commandBuffer, index, srcView, dstView, extent, finalLayout ); }; } RecordContext::ImplicitAction RecordContext::blitImage( ImageViewId srcView , ImageViewId dstView , Rect2D const & srcRect , Rect2D const & dstRect , FilterMode filter , ImageLayout finalLayout ) { return [srcView, dstView, srcRect, dstRect, filter, finalLayout]( RecordContext & recContext , VkCommandBuffer commandBuffer , uint32_t index ) { recContext.blitImage( commandBuffer, index, srcView, dstView, srcRect, dstRect, filter, finalLayout ); }; } RecordContext::ImplicitAction RecordContext::clearAttachment( Attachment const & attach , ImageLayout finalLayout ) { return [&attach, finalLayout]( RecordContext & recContext , VkCommandBuffer commandBuffer , uint32_t index ) { auto dstView = attach.view( index ); if ( isColourFormat( getFormat( dstView ) ) ) recContext.clearAttachment( commandBuffer, dstView, getClearColorValue( attach.getClearValue() ), finalLayout ); else recContext.clearAttachment( commandBuffer, dstView, getClearDepthStencilValue( attach.getClearValue() ), finalLayout ); }; } RecordContext::ImplicitAction RecordContext::clearAttachment( ImageViewId dstView , ClearColorValue const & clearValue , ImageLayout finalLayout ) { return [clearValue, dstView, finalLayout]( RecordContext & recContext , VkCommandBuffer commandBuffer , [[maybe_unused]] uint32_t index ) { recContext.clearAttachment( commandBuffer, dstView, clearValue, finalLayout ); }; } RecordContext::ImplicitAction RecordContext::clearAttachment( ImageViewId dstView , ClearDepthStencilValue const & clearValue , ImageLayout finalLayout ) { return [clearValue, dstView, finalLayout]( RecordContext & recContext , VkCommandBuffer commandBuffer , [[maybe_unused]] uint32_t index ) { recContext.clearAttachment( commandBuffer, dstView, clearValue, finalLayout ); }; } RecordContext::ImplicitAction RecordContext::clearBuffer( BufferViewId dstView , AccessState const & finalState ) { return clearBuffer( dstView, 0u, finalState ); } RecordContext::ImplicitAction RecordContext::clearBuffer( BufferViewId dstView , uint32_t clearValue , AccessState const & finalState ) { return [clearValue, dstView, finalState]( RecordContext & recContext , VkCommandBuffer commandBuffer , [[maybe_unused]] uint32_t index ) { recContext.clearBuffer( commandBuffer, dstView, clearValue, finalState ); }; } RecordContext::ImplicitAction RecordContext::copyBuffer( BufferViewId srcView , BufferViewId dstView , DeviceSize srcOffset, DeviceSize dstOffset , DeviceSize size , AccessState const & finalState ) { return [srcOffset, dstOffset, size, srcView, dstView, finalState]( RecordContext & recContext , VkCommandBuffer commandBuffer , uint32_t index ) { recContext.copyBuffer( commandBuffer, index, srcView, dstView, srcOffset, dstOffset, size, finalState ); }; } //************************************************************************************************ } ================================================ FILE: source/RenderGraph/ResourceHandler.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/ResourceHandler.hpp" #include "RenderGraph/Attachment.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/BufferData.hpp" #include "RenderGraph/BufferViewData.hpp" #include "RenderGraph/Hash.hpp" #include "RenderGraph/ImageData.hpp" #include "RenderGraph/ImageViewData.hpp" #include "RenderGraph/Log.hpp" #include "RenderGraph/RunnableGraph.hpp" #include #pragma warning( push ) #pragma warning( disable: 5262 ) #include #pragma warning( pop ) namespace crg { using lock_type = std::unique_lock< std::mutex >; //********************************************************************************************* namespace reshdl { struct Quad { using Data = std::array< float, 2u >; struct Vertex { Data position; Data texture; }; }; static VkBufferCreateInfo convert( BufferData const & data ) { return convert( data.info ); } static VkBufferViewCreateInfo convert( BufferViewData const & data , VkBuffer buffer ) { auto result = convert( data.info ); result.buffer = buffer; return result; } static VkImageCreateInfo convert( ImageData const & data ) { return convert( data.info ); } static VkImageViewCreateInfo convert( ImageViewData const & data , VkImage image ) { auto result = convert( data.info ); result.image = image; return result; } static size_t makeHash( SamplerDesc const & samplerDesc ) { auto result = std::hash< FilterMode >{}( samplerDesc.magFilter ); result = hashCombine( result, samplerDesc.minFilter ); result = hashCombine( result, samplerDesc.mipmapMode ); result = hashCombine( result, samplerDesc.addressModeU ); result = hashCombine( result, samplerDesc.addressModeV ); result = hashCombine( result, samplerDesc.addressModeW ); result = hashCombine( result, samplerDesc.mipLodBias ); result = hashCombine( result, samplerDesc.minLod ); result = hashCombine( result, samplerDesc.maxLod ); return result; } static size_t makeHash( bool texCoords , Texcoord const & config ) { size_t result{ ( ( texCoords ? 0x01u : 0x00u ) << 0u ) | ( ( config.invertU ? 0x01u : 0x00u ) << 1u ) | ( ( config.invertV ? 0x01u : 0x00u ) << 2u ) }; return result; } } //********************************************************************************************* ResourceHandler::~ResourceHandler()noexcept { for ( auto const & [data, _] : m_bufferViews ) { std::stringstream stream; stream << "Leaked [VkBufferView](" << data.data->name << ")"; Logger::logError( stream.str() ); } for ( auto const & [data, _] : m_buffers ) { std::stringstream stream; stream << "Leaked [VkBuffer](" << data.data->name << ")"; Logger::logError( stream.str() ); } for ( auto const & [data, _] : m_imageViews ) { std::stringstream stream; stream << "Leaked [VkImageView](" << data.data->name << ")"; Logger::logError( stream.str() ); } for ( auto const & [data, _] : m_images ) { std::stringstream stream; stream << "Leaked [VkImage](" << data.data->name << ")"; Logger::logError( stream.str() ); } for ( auto const & [_, data] : m_samplers ) { std::stringstream stream; stream << "Leaked [VkSampler](" << data.name << ")"; Logger::logError( stream.str() ); } } BufferId ResourceHandler::createBufferId( BufferData const & img ) { lock_type lock( m_buffersMutex ); auto data = std::make_unique< BufferData >( img ); BufferId result{ uint32_t( m_bufferIds.size() + 1u ), data.get() }; m_bufferIds.try_emplace( result, std::move( data ) ); return result; } BufferViewId ResourceHandler::createViewId( BufferViewData const & view ) { lock_type lock( m_bufferViewsMutex ); auto it = std::find_if( m_bufferViewIds.begin() , m_bufferViewIds.end() , [&view]( BufferViewIdDataOwnerCont::value_type const & lookup ) { return *lookup.second == view; } ); BufferViewId result{}; if ( it == m_bufferViewIds.end() ) { auto data = std::make_unique< BufferViewData >( view ); result = BufferViewId{ uint32_t( m_bufferViewIds.size() + 1u ), data.get() }; m_bufferViewIds.try_emplace( result, std::move( data ) ); } else { result = it->first; } return result; } ResourceHandler::CreatedT< VkBuffer > ResourceHandler::createBuffer( GraphContext & context , BufferId bufferId ) { ResourceHandler::CreatedT< VkBuffer > result{}; if ( context.vkCreateBuffer ) { lock_type lock( m_buffersMutex ); auto [it, ins] = m_buffers.try_emplace( bufferId, std::pair< VkBuffer, VkDeviceMemory >{} ); if ( ins && context.device ) { // Create buffer auto createInfo = reshdl::convert( *bufferId.data ); auto res = context.vkCreateBuffer( context.device , &createInfo , context.allocator , &it->second.first ); result.resource = it->second.first; checkVkResult( res, "Buffer creation" ); crgRegisterObjectName( context, bufferId.data->name, result.resource ); // Create Buffer memory VkMemoryRequirements requirements{}; context.vkGetBufferMemoryRequirements( context.device , result.resource , &requirements ); uint32_t deduced = context.deduceMemoryType( requirements.memoryTypeBits , getMemoryPropertyFlags( bufferId.data->info.memory ) ); VkMemoryAllocateInfo allocateInfo{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO , nullptr , requirements.size , deduced }; res = context.vkAllocateMemory( context.device , &allocateInfo , context.allocator , &it->second.second ); result.memory = it->second.second; checkVkResult( res, "Buffer memory allocation" ); crgRegisterObjectName( context, bufferId.data->name, result.memory ); // Bind buffer and memory res = context.vkBindBufferMemory( context.device , result.resource , result.memory , 0u ); checkVkResult( res, "Buffer memory binding" ); result.created = true; } else { result.resource = it->second.first; result.memory = it->second.second; } } return result; } ResourceHandler::CreatedViewT< VkBufferView > ResourceHandler::createBufferView( GraphContext & context , BufferViewId view ) { ResourceHandler::CreatedViewT< VkBufferView > result{}; if ( context.vkCreateBufferView ) { lock_type lock( m_bufferViewsMutex ); auto [it, ins] = m_bufferViews.try_emplace( view, VkBufferView{} ); if ( ins ) { auto buffer = createBuffer( context, view.data->buffer ).resource; auto createInfo = reshdl::convert( *view.data, buffer ); auto res = context.vkCreateBufferView( context.device , &createInfo , context.allocator , &it->second ); checkVkResult( res, "BufferView creation" ); crgRegisterObjectName( context, view.data->name, it->second ); result.view = it->second; result.created = true; } else { result.view = it->second; } } return result; } ImageId ResourceHandler::createImageId( ImageData const & img ) { lock_type lock( m_imagesMutex ); auto data = std::make_unique< ImageData >( img ); ImageId result{ uint32_t( m_imageIds.size() + 1u ), data.get() }; m_imageIds.try_emplace( result, std::move( data ) ); return result; } ImageViewId ResourceHandler::createViewId( ImageViewData const & view ) { lock_type lock( m_imageViewsMutex ); auto it = std::find_if( m_imageViewIds.begin() , m_imageViewIds.end() , [&view]( ImageViewIdDataOwnerCont::value_type const & lookup ) { return *lookup.second == view; } ); ImageViewId result{}; if ( it == m_imageViewIds.end() ) { auto data = std::make_unique< ImageViewData >( view ); result = ImageViewId{ uint32_t( m_imageViewIds.size() + 1u ), data.get() }; m_imageViewIds.try_emplace( result, std::move( data ) ); } else { result = it->first; } return result; } ResourceHandler::CreatedT< VkImage > ResourceHandler::createImage( GraphContext & context , ImageId imageId ) { ResourceHandler::CreatedT< VkImage > result{}; if ( context.vkCreateImage ) { lock_type lock( m_imagesMutex ); auto [it, ins] = m_images.try_emplace( imageId, std::pair< VkImage, VkDeviceMemory >{} ); if ( ins && context.device ) { // Create image auto createInfo = reshdl::convert( *imageId.data ); auto res = context.vkCreateImage( context.device , &createInfo , context.allocator , &it->second.first ); result.resource = it->second.first; checkVkResult( res, "Image creation" ); crgRegisterObjectName( context, imageId.data->name, result.resource ); // Create Image memory VkMemoryRequirements requirements{}; context.vkGetImageMemoryRequirements( context.device , result.resource , &requirements ); uint32_t deduced = context.deduceMemoryType( requirements.memoryTypeBits , getMemoryPropertyFlags( imageId.data->info.memory ) ); VkMemoryAllocateInfo allocateInfo{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO , nullptr , requirements.size , deduced }; res = context.vkAllocateMemory( context.device , &allocateInfo , context.allocator , &it->second.second ); result.memory = it->second.second; checkVkResult( res, "Image memory allocation" ); crgRegisterObjectName( context, imageId.data->name, result.memory ); // Bind image and memory res = context.vkBindImageMemory( context.device , result.resource , result.memory , 0u ); checkVkResult( res, "Image memory binding" ); result.created = true; } else { result.resource = it->second.first; result.memory = it->second.second; } } return result; } ResourceHandler::CreatedViewT< VkImageView > ResourceHandler::createImageView( GraphContext & context , ImageViewId view ) { ResourceHandler::CreatedViewT< VkImageView > result{}; if ( context.vkCreateImageView ) { lock_type lock( m_bufferViewsMutex ); auto [it, ins] = m_imageViews.try_emplace( view, VkImageView{} ); if ( ins ) { auto image = createImage( context, view.data->image ).resource; auto createInfo = reshdl::convert( *view.data, image ); auto res = context.vkCreateImageView( context.device , &createInfo , context.allocator , &it->second ); checkVkResult( res, "ImageView creation" ); crgRegisterObjectName( context, view.data->name, it->second ); result.view = it->second; result.created = true; } else { result.view = it->second; } } return result; } VkSampler ResourceHandler::createSampler( GraphContext & context , std::string const & suffix , SamplerDesc const & samplerDesc ) { VkSampler result{}; if ( context.vkCreateSampler ) { lock_type lock( m_samplersMutex ); VkSamplerCreateInfo createInfo{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO , nullptr , 0u , convert( samplerDesc.magFilter ) , convert( samplerDesc.minFilter ) , convert( samplerDesc.mipmapMode ) , convert( samplerDesc.addressModeU ) , convert( samplerDesc.addressModeV ) , convert( samplerDesc.addressModeW ) , samplerDesc.mipLodBias // mipLodBias , VK_FALSE // anisotropyEnable , 0.0f // maxAnisotropy , VK_FALSE // compareEnable , VK_COMPARE_OP_ALWAYS // compareOp , samplerDesc.minLod , samplerDesc.maxLod , VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK , VK_FALSE }; auto res = context.vkCreateSampler( context.device , &createInfo , context.allocator , &result ); auto & sampler = m_samplers.try_emplace( result, Sampler{ result, {} } ).first->second; checkVkResult( res, "Sampler creation" ); sampler.name = "Sampler_" + suffix; crgRegisterObject( context, sampler.name, result ); } return result; } VertexBuffer const * ResourceHandler::createQuadTriVertexBuffer( GraphContext & context , std::string const & suffix , bool texCoords , Texcoord const & config ) { VertexBuffer * vertexBuffer{}; lock_type lock( m_vertexBuffersMutex ); if ( context.vkCreateBuffer ) { auto bufferId = createBufferId( BufferData{ "QuadVertexMemory_" + suffix , BufferCreateFlags::eNone , 3u * sizeof( reshdl::Quad::Vertex ) , BufferUsageFlags::eVertexBuffer , MemoryPropertyFlags::eHostVisible } ); auto result = std::make_unique< VertexBuffer >( createViewId( BufferViewData{ "QuadVertexMemory_" + suffix , bufferId , { 0u, bufferId.data->info.size } } ) ); vertexBuffer = result.get(); if ( context.device ) { auto created = createBuffer( context, vertexBuffer->buffer.data->buffer ); reshdl::Quad::Vertex * buffer{}; auto res = context.vkMapMemory( context.device , created.memory , 0u , VK_WHOLE_SIZE , 0u , reinterpret_cast< void ** >( &buffer ) ); checkVkResult( res, "Buffer memory mapping" ); if ( buffer ) { auto rangeU = 1.0; auto minU = 0.0; auto maxU = minU + 2.0 * rangeU; auto rangeV = 1.0; auto minV = -rangeV; auto maxV = minV + 2.0 * rangeV; auto realMinU = float( config.invertU ? maxU : minU ); auto realMaxU = float( config.invertU ? minU : maxU ); auto realMinV = float( config.invertV ? maxV : minV ); auto realMaxV = float( config.invertV ? minV : maxV ); std::array vertexData { reshdl::Quad::Vertex{ reshdl::Quad::Data{ -1.0f, -3.0f }, reshdl::Quad::Data{ realMinU, realMinV } } , reshdl::Quad::Vertex{ reshdl::Quad::Data{ -1.0f, +1.0f }, reshdl::Quad::Data{ realMinU, realMaxV } } , reshdl::Quad::Vertex{ reshdl::Quad::Data{ +3.0f, +1.0f }, reshdl::Quad::Data{ realMaxU, realMaxV } } }; std::copy( vertexData.begin(), vertexData.end(), buffer ); VkMappedMemoryRange memoryRange{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE , nullptr , created.memory , 0u , VK_WHOLE_SIZE }; context.vkFlushMappedMemoryRanges( context.device, 1u, &memoryRange ); context.vkUnmapMemory( context.device, created.memory ); } vertexBuffer->vertexAttribs.push_back( { 0u, 0u, VK_FORMAT_R32G32_SFLOAT, offsetof( reshdl::Quad::Vertex, position ) } ); if ( texCoords ) { vertexBuffer->vertexAttribs.push_back( { 1u, 0u, VK_FORMAT_R32G32_SFLOAT, offsetof( reshdl::Quad::Vertex, texture ) } ); } vertexBuffer->vertexBindings.push_back( { 0u, sizeof( reshdl::Quad::Vertex ), VK_VERTEX_INPUT_RATE_VERTEX } ); vertexBuffer->inputState = VkPipelineVertexInputStateCreateInfo{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO , nullptr , 0u , uint32_t( vertexBuffer->vertexBindings.size() ) , vertexBuffer->vertexBindings.data() , uint32_t( vertexBuffer->vertexAttribs.size() ) , vertexBuffer->vertexAttribs.data() }; } m_vertexBuffers.emplace( std::move( result ) ); } return vertexBuffer; } void ResourceHandler::destroyBuffer( GraphContext & context , BufferId bufferId ) { lock_type lock( m_buffersMutex ); auto it = m_buffers.find( bufferId ); if ( it != m_buffers.end() ) { if ( context.vkDestroyBuffer && it->second.first ) { context.vkDestroyBuffer( context.device, it->second.first, context.allocator ); } if ( context.vkFreeMemory && it->second.second ) { context.vkFreeMemory( context.device, it->second.second, context.allocator ); } m_buffers.erase( it ); } } void ResourceHandler::destroyBufferView( GraphContext & context , BufferViewId viewId ) { lock_type lock( m_bufferViewsMutex ); auto it = m_bufferViews.find( viewId ); if ( it != m_bufferViews.end() ) { if ( context.vkDestroyBufferView && it->second ) { context.vkDestroyBufferView( context.device, it->second, context.allocator ); } m_bufferViews.erase( it ); } } void ResourceHandler::destroyImage( GraphContext & context , ImageId imageId ) { lock_type lock( m_imagesMutex ); auto it = m_images.find( imageId ); if ( it != m_images.end() ) { if ( context.vkDestroyImage && it->second.first ) { context.vkDestroyImage( context.device, it->second.first, context.allocator ); } if ( context.vkFreeMemory && it->second.second ) { context.vkFreeMemory( context.device, it->second.second, context.allocator ); } m_images.erase( it ); } } void ResourceHandler::destroyImageView( GraphContext & context , ImageViewId viewId ) { lock_type lock( m_bufferViewsMutex ); auto it = m_imageViews.find( viewId ); if ( it != m_imageViews.end() ) { if ( context.vkDestroyImageView && it->second ) { context.vkDestroyImageView( context.device, it->second, context.allocator ); } m_imageViews.erase( it ); } } void ResourceHandler::destroySampler( GraphContext & context , VkSampler sampler ) { lock_type lock( m_samplersMutex ); auto it = m_samplers.find( sampler ); if ( it != m_samplers.end() ) { if ( context.vkDestroySampler && it->first ) { crgUnregisterObject( context, it->first ); context.vkDestroySampler( context.device , it->first , context.allocator ); } m_samplers.erase( it ); } } void ResourceHandler::destroyVertexBuffer( GraphContext & context , VertexBuffer const * buffer ) { lock_type lock( m_vertexBuffersMutex ); auto it = std::find_if( m_vertexBuffers.begin() , m_vertexBuffers.end() , [buffer]( VertexBufferPtr const & lookup ) { return lookup.get() == buffer; } ); if ( it != m_vertexBuffers.end() ) { auto const & vertexBuffer = **it; destroyBuffer( context, vertexBuffer.buffer.data->buffer ); m_vertexBuffers.erase( it ); } } //********************************************************************************************* ContextResourcesCache::ContextResourcesCache( ResourceHandler & handler , GraphContext & context ) : m_handler{ handler } , m_context{ context } { } ContextResourcesCache::~ContextResourcesCache()noexcept { for ( auto const & [bufferView, _] : m_bufferViews ) { m_handler.destroyBufferView( m_context, bufferView ); } for ( auto const & [buffer, _] : m_buffers ) { m_handler.destroyBuffer( m_context, buffer ); } for ( auto const & [imageView, _] : m_imageViews ) { m_handler.destroyImageView( m_context, imageView ); } for ( auto const & [image, _] : m_images ) { m_handler.destroyImage( m_context, image ); } for ( auto const & [_, sampler] : m_samplers ) { m_handler.destroySampler( m_context, sampler ); } for ( auto const & [_, buffer] : m_vertexBuffers ) { m_handler.destroyVertexBuffer( m_context, buffer ); } } VkBuffer ContextResourcesCache::createBuffer( BufferId const & buffer ) { VkDeviceMemory memory{}; return createBuffer( buffer, memory ); } VkBuffer ContextResourcesCache::createBuffer( BufferId const & buffer, VkDeviceMemory & memory ) { auto [created, result, mem] = m_handler.createBuffer( m_context, buffer ); if ( created ) { m_buffers[buffer] = result; } memory = mem; return result; } VkBufferView ContextResourcesCache::createBufferView( BufferViewId const & view ) { auto [created, result] = m_handler.createBufferView( m_context, view ); if ( created ) { m_bufferViews[view] = result; } return result; } bool ContextResourcesCache::destroyBuffer( BufferId const & bufferId ) { auto it = m_buffers.find( bufferId ); auto result = it != m_buffers.end(); if ( result ) { m_handler.destroyBuffer( m_context, bufferId ); } return result; } bool ContextResourcesCache::destroyBufferView( BufferViewId const & viewId ) { auto it = m_bufferViews.find( viewId ); auto result = it != m_bufferViews.end(); if ( result ) { m_handler.destroyBufferView( m_context, viewId ); } return result; } VkImage ContextResourcesCache::createImage( ImageId const & image ) { VkDeviceMemory memory{}; return createImage( image, memory ); } VkImage ContextResourcesCache::createImage( ImageId const & image, VkDeviceMemory & memory ) { auto [created, result, mem] = m_handler.createImage( m_context, image ); if ( created ) { m_images[image] = result; } memory = mem; return result; } VkImageView ContextResourcesCache::createImageView( ImageViewId const & view ) { auto [created, result] = m_handler.createImageView( m_context, view ); if ( created ) { m_imageViews[view] = result; } return result; } bool ContextResourcesCache::destroyImage( ImageId const & imageId ) { auto it = m_images.find( imageId ); auto result = it != m_images.end(); if ( result ) { m_handler.destroyImage( m_context, imageId ); } return result; } bool ContextResourcesCache::destroyImageView( ImageViewId const & viewId ) { auto it = m_imageViews.find( viewId ); auto result = it != m_imageViews.end(); if ( result ) { m_handler.destroyImageView( m_context, viewId ); } return result; } VkSampler ContextResourcesCache::createSampler( SamplerDesc const & samplerDesc ) { auto hash = reshdl::makeHash( samplerDesc ); auto [it, res] = m_samplers.try_emplace( hash, VkSampler{} ); if ( res ) { it->second = m_handler.createSampler( m_context , std::to_string( hash ) , samplerDesc ); } return it->second; } VertexBuffer const & ContextResourcesCache::createQuadTriVertexBuffer( bool texCoords , Texcoord const & config ) { auto hash = reshdl::makeHash( texCoords, config ); auto [it, res] = m_vertexBuffers.emplace( hash, nullptr ); if ( res ) { it->second = m_handler.createQuadTriVertexBuffer( m_context , std::to_string( hash ) , texCoords , config ); } return *it->second; } //********************************************************************************************* ResourcesCache::ResourcesCache( ResourceHandler & handler ) : m_handler{ handler } { } VkBuffer ResourcesCache::createBuffer( GraphContext & context , BufferId const & bufferId , VkDeviceMemory & memory ) { auto & cache = getContextCache( context ); return cache.createBuffer( bufferId, memory ); } VkBuffer ResourcesCache::createBuffer( GraphContext & context , BufferId const & bufferId ) { auto & cache = getContextCache( context ); return cache.createBuffer( bufferId ); } VkBufferView ResourcesCache::createBufferView( GraphContext & context , BufferViewId const & viewId ) { auto & cache = getContextCache( context ); return cache.createBufferView( viewId ); } bool ResourcesCache::destroyBuffer( BufferId const & bufferId ) { auto it = std::find_if( m_caches.begin() , m_caches.end() , [&bufferId]( ContextCacheMap::value_type & lookup ) { return lookup.second.destroyBuffer( bufferId ); } ); return it != m_caches.end(); } bool ResourcesCache::destroyBufferView( BufferViewId const & viewId ) { auto it = std::find_if( m_caches.begin() , m_caches.end() , [&viewId]( ContextCacheMap::value_type & lookup ) { return lookup.second.destroyBufferView( viewId ); } ); return it != m_caches.end(); } bool ResourcesCache::destroyBuffer( GraphContext & context , BufferId const & bufferId ) { auto & cache = getContextCache( context ); return cache.destroyBuffer( bufferId ); } bool ResourcesCache::destroyBufferView( GraphContext & context , BufferViewId const & viewId ) { auto & cache = getContextCache( context ); return cache.destroyBufferView( viewId ); } VkImage ResourcesCache::createImage( GraphContext & context , ImageId const & imageId , VkDeviceMemory & memory ) { auto & cache = getContextCache( context ); return cache.createImage( imageId, memory ); } VkImage ResourcesCache::createImage( GraphContext & context , ImageId const & imageId ) { auto & cache = getContextCache( context ); return cache.createImage( imageId ); } VkImageView ResourcesCache::createImageView( GraphContext & context , ImageViewId const & viewId ) { auto & cache = getContextCache( context ); return cache.createImageView( viewId ); } bool ResourcesCache::destroyImage( ImageId const & imageId ) { auto it = std::find_if( m_caches.begin() , m_caches.end() , [&imageId]( ContextCacheMap::value_type & lookup ) { return lookup.second.destroyImage( imageId ); } ); return it != m_caches.end(); } bool ResourcesCache::destroyImageView( ImageViewId const & viewId ) { auto it = std::find_if( m_caches.begin() , m_caches.end() , [&viewId]( ContextCacheMap::value_type & lookup ) { return lookup.second.destroyImageView( viewId ); } ); return it != m_caches.end(); } bool ResourcesCache::destroyImage( GraphContext & context , ImageId const & imageId ) { auto & cache = getContextCache( context ); return cache.destroyImage( imageId ); } bool ResourcesCache::destroyImageView( GraphContext & context , ImageViewId const & viewId ) { auto & cache = getContextCache( context ); return cache.destroyImageView( viewId ); } VkSampler ResourcesCache::createSampler( GraphContext & context , SamplerDesc const & samplerDesc ) { auto & cache = getContextCache( context ); return cache.createSampler( samplerDesc ); } VertexBuffer const & ResourcesCache::createQuadTriVertexBuffer( GraphContext & context , bool texCoords , Texcoord const & config ) { auto & cache = getContextCache( context ); return cache.createQuadTriVertexBuffer( texCoords, config ); } ContextResourcesCache & ResourcesCache::getContextCache( GraphContext & context ) { auto it = m_caches.find( &context ); if ( it == m_caches.end() ) { it = m_caches.try_emplace( &context, m_handler, context ).first; } return it->second; } //********************************************************************************************* } ================================================ FILE: source/RenderGraph/RunnableGraph.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/RunnableGraph.hpp" #include "RenderGraph/GraphVisitor.hpp" #include "RenderGraph/Log.hpp" #include "RenderGraph/ResourceHandler.hpp" #include #include #include #include #include #pragma warning( push ) #pragma warning( disable: 5262 ) #include #pragma warning( pop ) #pragma GCC diagnostic ignored "-Wnull-dereference" namespace crg { //************************************************************************************************ namespace rungrf { static VkCommandPool createCommandPool( GraphContext & context , std::string const & name ) { VkCommandPool result{}; if ( context.vkCreateCommandPool ) { VkCommandPoolCreateInfo createInfo{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO , nullptr , VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT , 0 }; auto res = context.vkCreateCommandPool( context.device , &createInfo , context.allocator , &result ); checkVkResult( res, name + " - CommandPool creation" ); crgRegisterObject( context, name, result ); } return result; } static void mergeMipRanges( LayerLayoutStates const & nextLayout , uint32_t currentLayout , MipLayoutStates const & curStates , LayerLayoutStates & result ) { if ( auto nextLayerIt = nextLayout.find( currentLayout ); nextLayerIt != nextLayout.end() ) { auto resLayerIt = result.try_emplace( currentLayout ).first; for ( auto & [curLevel, _] : curStates ) { if ( auto nextLevelIt = nextLayerIt->second.find( curLevel ); nextLevelIt != nextLayerIt->second.end() ) resLayerIt->second.emplace( *nextLevelIt ); } } } static LayerLayoutStates mergeRanges( LayerLayoutStatesMap const & nextLayouts , LayerLayoutStatesMap::value_type const & currentLayout ) { LayerLayoutStates result; if ( auto nextIt = nextLayouts.find( currentLayout.first ); nextIt != nextLayouts.end() ) { auto & nxtLayout = nextIt->second; for ( auto & [curLayout, curStates] : currentLayout.second ) mergeMipRanges( nxtLayout, curLayout, curStates, result ); } return result; } static LayerLayoutStatesMap gatherNextImageLayouts( LayerLayoutStatesMap currentLayouts , std::vector< RunnablePassPtr >::iterator nextPassIt , std::vector< RunnablePassPtr >::iterator endIt ) { LayerLayoutStatesMap result; while ( !currentLayouts.empty() && endIt != nextPassIt ) { if ( auto const & nextPass = **nextPassIt; nextPass.isEnabled() ) { auto & nextLayouts = nextPass.getImageLayouts(); LayerLayoutStates layoutStates; auto currIt = std::find_if( currentLayouts.begin() , currentLayouts.end() , [&nextLayouts, &layoutStates]( LayerLayoutStatesMap::value_type const & lookup ) { layoutStates = mergeRanges( nextLayouts, lookup ); return !layoutStates.empty(); } ); if ( currIt != currentLayouts.end() ) { result.try_emplace( currIt->first, layoutStates ); currentLayouts.erase( currIt ); } } ++nextPassIt; } return result; } static PipelineState getNextState( PipelineState currentState , std::vector< RunnablePassPtr >::iterator nextPassIt , std::vector< RunnablePassPtr >::iterator endIt ) { while ( endIt != nextPassIt && !( *nextPassIt )->isEnabled() ) { ++nextPassIt; } return ( endIt != nextPassIt && ( *nextPassIt )->isEnabled() ) ? ( *nextPassIt )->getPipelineState() : currentState; } static VkDescriptorType getDescriptorType( BufferAttachment const & attach ) { if ( attach.isUniformView() ) return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; if ( attach.isStorageView() ) return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; if ( attach.isUniform() ) return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; } static VkDescriptorType getDescriptorType( ImageAttachment const & attach ) { if ( attach.isStorageView() ) return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; } static WriteDescriptorSet getWrite( ImageAttachment const & attach , SamplerDesc const & samplerDesc , uint32_t binding , uint32_t count , uint32_t index , RunnableGraph & graph ) { WriteDescriptorSet result{ binding , 0u , count , getDescriptorType( attach ) }; VkSampler sampler = attach.isSampledView() ? graph.createSampler( samplerDesc ) : VkSampler{}; VkImageLayout layout = attach.isSampledView() ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL; VkDescriptorImageInfo info{ sampler , graph.createImageView( attach.view( index ) ) , layout }; result.imageInfo.push_back( info ); return result; } static WriteDescriptorSet getWrite( BufferAttachment const & attach , uint32_t binding , uint32_t count , uint32_t index , RunnableGraph & graph ) { WriteDescriptorSet result{ binding , 0u , count , getDescriptorType( attach ) }; auto bufferViewId = attach.buffer( index ); VkDescriptorBufferInfo info{ graph.createBuffer( bufferViewId.data->buffer ) , getSubresourceRange( bufferViewId ).offset , getSubresourceRange( bufferViewId ).size }; if ( attach.isView() ) { result.bufferViewInfo.push_back( info ); result.texelBufferView.push_back( graph.createBufferView( bufferViewId ) ); } else { result.bufferInfo.push_back( info ); } return result; } } //************************************************************************************************ VkQueryPool createQueryPool( GraphContext & context , std::string const & name , uint32_t passesCount ) { VkQueryPool result{}; if ( context.vkCreateQueryPool ) { VkQueryPoolCreateInfo createInfo{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO , nullptr , 0u , VK_QUERY_TYPE_TIMESTAMP , passesCount , 0u }; auto res = context.vkCreateQueryPool( context.device , &createInfo , context.allocator , &result ); checkVkResult( res, name + " - VkQueryPool creation" ); crgRegisterObject( context, name, result ); } return result; } //************************************************************************************************ RunnableGraph::RunnableGraph( FrameGraph & graph , GraphNodePtrArray nodes , RootNode rootNode , GraphContext & context ) : m_graph{ graph } , m_context{ context } , m_resources{ m_graph.getHandler(), m_context } , m_nodes{ std::move( nodes ) } , m_rootNode{ std::move( rootNode ) } , m_timerQueries{ m_context , createQueryPool( m_context, m_graph.getName() + "TimerQueries", uint32_t( ( m_nodes.size() + 1u ) * 2u * 2u ) ) , []( GraphContext & ctx, VkQueryPool & object )noexcept { crgUnregisterObject( ctx, object ); ctx.vkDestroyQueryPool( ctx.device, object, ctx.allocator ); object = {}; } } , m_commandPool{ m_context , rungrf::createCommandPool( m_context, m_graph.getName() ) , []( GraphContext & ctx, VkCommandPool & object )noexcept { crgUnregisterObject( ctx, object ); ctx.vkDestroyCommandPool( ctx.device, object, ctx.allocator ); object = {}; } } , m_fence{ m_context , graph.getName() + "/Graph" , { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT } } , m_timer{ context, graph.getName() + "/Graph", TimerScope::eGraph, getTimerQueryPool(), getTimerQueryOffset() } { auto name = m_graph.getName() + "/Graph"; if ( m_context.vkCreateSemaphore ) { VkSemaphoreCreateInfo createInfo{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO , nullptr , 0u }; auto res = context.vkCreateSemaphore( m_context.device , &createInfo , m_context.allocator , &m_semaphore ); checkVkResult( res, name + " - Semaphore creation" ); crgRegisterObject( m_context, name, m_semaphore ); } if ( m_context.vkAllocateCommandBuffers ) { VkCommandBufferAllocateInfo allocateInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO , nullptr , getCommandPool() , VK_COMMAND_BUFFER_LEVEL_PRIMARY , 1u }; auto res = m_context.vkAllocateCommandBuffers( m_context.device , &allocateInfo , &m_commandBuffer ); checkVkResult( res, name + " - CommandBuffer allocation" ); crgRegisterObject( m_context, name, m_commandBuffer ); VkCommandBufferBeginInfo beginInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO , nullptr , VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT , nullptr }; m_context.vkBeginCommandBuffer( m_commandBuffer, &beginInfo ); m_context.vkEndCommandBuffer( m_commandBuffer ); } Logger::logDebug( m_graph.getName() + " - Initialising resources" ); for ( auto & img : m_graph.m_images ) { m_resources.createImage( img ); } for ( auto & view : m_graph.m_imageViews ) { m_resources.createImageView( view ); } Logger::logDebug( m_graph.getName() + " - Creating runnable passes" ); for ( auto const & node : m_nodes ) { if ( node->getKind() == GraphNode::Kind::FramePass ) { auto const & renderPassNode = nodeCast< FramePassNode >( *node ); m_passes.push_back( renderPassNode.getFramePass().createRunnable( m_context , *this ) ); } } Logger::logDebug( m_graph.getName() + " - Initialising passes" ); for ( auto const & pass : m_passes ) { if ( pass->isEnabled() ) { pass->initialise( pass->getIndex() ); } } } RunnableGraph::~RunnableGraph()noexcept { if ( m_context.vkDestroySemaphore && m_semaphore ) { crgUnregisterObject( m_context, m_semaphore ); m_context.vkDestroySemaphore( m_context.device , m_semaphore , m_context.allocator ); } if ( m_context.vkFreeCommandBuffers && m_commandBuffer ) { crgUnregisterObject( m_context, m_commandBuffer ); m_context.vkFreeCommandBuffers( m_context.device , getCommandPool() , 1u , &m_commandBuffer ); } } void RunnableGraph::record() { auto block( m_timer.start() ); m_states.clear(); RecordContext recordContext{ m_resources }; for ( auto & dependency : m_graph.getDependencies() ) { recordContext.addStates( dependency->getFinalStates() ); m_states.try_emplace( dependency , dependency->getFinalStates().getIndexState() ); } auto itGraph = m_states.try_emplace( &m_graph ).first; itGraph->second.resize( m_passes.size() ); if ( !m_passes.empty() ) { auto it = itGraph->second.begin(); auto currPass = m_passes.begin(); recordContext.setNextPipelineState( ( *currPass )->getPipelineState() , ( *currPass )->getImageLayouts() ); auto nextPass = std::next( currPass ); m_fence.wait( 0xFFFFFFFFFFFFFFFFULL ); m_context.vkResetCommandBuffer( m_commandBuffer, 0u ); VkCommandBufferBeginInfo beginInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO , nullptr , VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT , nullptr }; m_context.vkBeginCommandBuffer( m_commandBuffer, &beginInfo ); m_timer.beginPass( m_commandBuffer, getName(), 0u ); while ( currPass != m_passes.end() ) { auto const & pass = *currPass; ++currPass; if ( nextPass != m_passes.end() ) { recordContext.setNextPipelineState( rungrf::getNextState( pass->getPipelineState(), nextPass, m_passes.end() ) , rungrf::gatherNextImageLayouts( pass->getImageLayouts(), nextPass, m_passes.end() ) ); ++nextPass; } else { recordContext.setNextPipelineState( pass->getPipelineState() , m_graph.getOutputLayoutStates() ); } *it = pass->recordCurrentInto( recordContext, m_commandBuffer ); ++it; } m_timer.endPass( m_commandBuffer ); m_context.vkEndCommandBuffer( m_commandBuffer ); } m_graph.registerFinalState( recordContext ); } SemaphoreWaitArray RunnableGraph::run( VkQueue queue ) { return run( SemaphoreWaitArray{} , queue ); } SemaphoreWaitArray RunnableGraph::run( SemaphoreWait toWait , VkQueue queue ) { return run( ( toWait.semaphore ? SemaphoreWaitArray{ 1u, toWait } : SemaphoreWaitArray{} ) , queue ); } SemaphoreWaitArray RunnableGraph::run( SemaphoreWaitArray const & toWait , VkQueue queue ) { record(); std::vector< VkSemaphore > semaphores; std::vector< VkPipelineStageFlags > dstStageMasks; convert( toWait, semaphores, dstStageMasks ); m_timer.notifyPassRender(); for ( auto const & pass : m_passes ) { pass->notifyPassRender(); } VkSubmitInfo submitInfo{ VK_STRUCTURE_TYPE_SUBMIT_INFO , nullptr , uint32_t( semaphores.size() ) , semaphores.data() , dstStageMasks.data() , 1u , &m_commandBuffer , 1u , &m_semaphore }; m_fence.reset(); m_context.delQueue.clear( m_context ); m_context.vkQueueSubmit( queue , 1u , &submitInfo , m_fence.getInternal() ); return { SemaphoreWait{ m_semaphore , m_graph.getFinalStates().getCurrPipelineState().pipelineStage } }; } VkBuffer RunnableGraph::createBuffer( BufferId const & buffer ) { return m_resources.createBuffer( buffer ); } VkBufferView RunnableGraph::createBufferView( BufferViewId const & view ) { return m_resources.createBufferView( view ); } VkImage RunnableGraph::createImage( ImageId const & image ) { return m_resources.createImage( image ); } VkImageView RunnableGraph::createImageView( ImageViewId const & view ) { return m_resources.createImageView( view ); } VkSampler RunnableGraph::createSampler( SamplerDesc const & samplerDesc ) { return m_resources.createSampler( samplerDesc ); } VertexBuffer const & RunnableGraph::createQuadTriVertexBuffer( bool texCoords , Texcoord const & config ) { return m_resources.createQuadTriVertexBuffer( texCoords , config ); } LayoutState RunnableGraph::getCurrentLayoutState( RecordContext & context , ImageId image , ImageViewType viewType , ImageSubresourceRange range )const { auto result = context.getLayoutState( image, viewType, range ); if ( result.layout == ImageLayout::eUndefined ) { // Lookup in graph's external inputs. result = m_graph.getInputLayoutState( image, viewType, range ); if ( result.layout != ImageLayout::eUndefined ) { context.setLayoutState( image, viewType, range, result ); } } if ( result.layout == ImageLayout::eUndefined ) { // Lookup in graph's previous final state. result = m_graph.getFinalLayoutState( image, viewType, range ); if ( result.layout != ImageLayout::eUndefined ) { context.setLayoutState( image, viewType, range, result ); } } return result; } LayoutState RunnableGraph::getCurrentLayoutState( RecordContext & context , ImageViewId view )const { return getCurrentLayoutState( context , view.data->image , view.data->info.viewType , getSubresourceRange( view ) ); } LayoutState RunnableGraph::getNextLayoutState( RecordContext const & context , crg::RunnablePass const & runnable , ImageViewId view )const { auto result = context.getNextLayoutState( view ); if ( result.layout == ImageLayout::eUndefined ) { // Next layout undefined means that there is no pass after this one in the graph. result = getOutputLayoutState( view ); } if ( result.layout == ImageLayout::eUndefined ) { // Prevent from outputing a ImageLayout::eUndefined anyway. result = runnable.getLayoutState( view ); } return result; } LayoutState RunnableGraph::getOutputLayoutState( ImageViewId view )const { return m_graph.getOutputLayoutState( view ); } VkDescriptorType RunnableGraph::getDescriptorType( Attachment const & attach )const { if ( attach.isImage() ) return rungrf::getDescriptorType( attach.imageAttach ); return rungrf::getDescriptorType( attach.bufferAttach ); } WriteDescriptorSet RunnableGraph::getDescriptorWrite( Attachment const & attach, uint32_t binding, uint32_t index ) { if ( attach.isImage() ) return rungrf::getWrite( attach.imageAttach, SamplerDesc{}, binding, 1u, index, *this ); return rungrf::getWrite( attach.bufferAttach, binding, 1u, index, *this ); } WriteDescriptorSet RunnableGraph::getDescriptorWrite( Attachment const & attach, SamplerDesc const & samplerDesc, uint32_t binding, uint32_t index ) { assert( attach.isImage() ); return rungrf::getWrite( attach.imageAttach, samplerDesc, binding, 1u, index, *this ); } } ================================================ FILE: source/RenderGraph/RunnablePass.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePass.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/Log.hpp" #include "RenderGraph/RunnableGraph.hpp" #include #pragma warning( push ) #pragma warning( disable: 5262 ) #include #pragma warning( pop ) namespace crg { namespace details { static constexpr DeviceSize getAlignedSize( DeviceSize size, DeviceSize align ) { auto rem = size % align; return ( rem ? size + ( align - rem ) : size ); } static void registerImage( Attachment const & attach , bool isComputePass, bool separateDepthStencilLayouts , LayerLayoutStatesHandler & imageLayouts ) { for ( uint32_t i = 0u; i < attach.getViewCount(); ++i ) { auto view = attach.view( i ); if ( view.data->source.empty() ) { imageLayouts.setLayoutState( view , { attach.getImageLayout( separateDepthStencilLayouts ) , attach.getAccessMask() , attach.getPipelineStageFlags( isComputePass ) } ); } else { for ( auto & source : view.data->source ) { imageLayouts.setLayoutState( source , { attach.getImageLayout( separateDepthStencilLayouts ) , attach.getAccessMask() , attach.getPipelineStageFlags( isComputePass ) } ); } } } } static void registerBuffer( Attachment const & attach , bool isComputePass , AccessStateMap & bufferAccesses ) { for ( uint32_t i = 0u; i < attach.getBufferCount(); ++i ) { bufferAccesses.insert_or_assign( attach.buffer( i ).data->buffer.id , AccessState{ attach.getAccessMask() , attach.getPipelineStageFlags( isComputePass ) } ); } } static void registerResources( FramePass const & pass , RunnablePass::Callbacks const & callbacks , GraphContext const & graphContext , LayerLayoutStatesHandler & imageLayouts , AccessStateMap & bufferAccesses ) { bool isComputePass = callbacks.isComputePass(); for ( auto & [binding, attach] : pass.getUniforms() ) registerBuffer( *attach, isComputePass, bufferAccesses ); for ( auto & [binding, attach] : pass.getSampled() ) registerImage( *attach.attach, isComputePass, graphContext.separateDepthStencilLayouts, imageLayouts ); for ( auto & [binding, attach] : pass.getInputs() ) if ( attach->isImage() ) registerImage( *attach, isComputePass, graphContext.separateDepthStencilLayouts, imageLayouts ); else registerBuffer( *attach, isComputePass, bufferAccesses ); for ( auto & [binding, attach] : pass.getInouts() ) if ( attach->isImage() ) registerImage( *attach, isComputePass, graphContext.separateDepthStencilLayouts, imageLayouts ); else registerBuffer( *attach, isComputePass, bufferAccesses ); for ( auto & [binding, attach] : pass.getOutputs() ) if ( attach->isImage() ) registerImage( *attach, isComputePass, graphContext.separateDepthStencilLayouts, imageLayouts ); else registerBuffer( *attach, isComputePass, bufferAccesses ); for ( auto attach : pass.getTargets() ) registerImage( *attach, isComputePass, graphContext.separateDepthStencilLayouts, imageLayouts ); } static void prepareImage( VkCommandBuffer commandBuffer , uint32_t index , Attachment const & attach , bool separateDepthStencilLayouts , RunnableGraph & graph , RecordContext & recordContext ) { auto view = attach.view( index ); recordContext.runImplicitTransition( commandBuffer , index , view ); if ( !attach.isNoTransition() && ( attach.isSampledImageView() || attach.isStorageImageView() || attach.isTransferImageView() || attach.isTransitionImageView() ) ) { auto needed = makeLayoutState( attach.getImageLayout( separateDepthStencilLayouts ) ); auto currentLayout = ( !attach.isInput() ? crg::makeLayoutState( ImageLayout::eUndefined ) : graph.getCurrentLayoutState( recordContext, view ) ); checkUndefinedInput( "Record", attach, view, currentLayout.layout ); if ( attach.isClearableImage() ) { recordContext.memoryBarrier( commandBuffer , view , currentLayout.layout , makeLayoutState( ImageLayout::eTransferDst ) ); auto subresourceRange = convert( getSubresourceRange( view ) ); if ( isColourFormat( getFormat( view ) ) ) { VkClearColorValue colour{}; recordContext->vkCmdClearColorImage( commandBuffer , graph.createImage( view.data->image ) , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , &colour , 1u, &subresourceRange ); } else { VkClearDepthStencilValue depthStencil{}; recordContext->vkCmdClearDepthStencilImage( commandBuffer , graph.createImage( view.data->image ) , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , &depthStencil , 1u, &subresourceRange ); } currentLayout.layout = ImageLayout::eTransferDst; currentLayout.state.access = AccessFlags::eTransferWrite; currentLayout.state.pipelineStage = PipelineStageFlags::eTransfer; } recordContext.memoryBarrier( commandBuffer , view , currentLayout.layout , needed ); } } static void prepareBuffer( VkCommandBuffer commandBuffer , uint32_t index , Attachment const & attach , bool isComputePass , RunnableGraph & graph , RecordContext & recordContext ) { auto view = attach.buffer( index ); recordContext.runImplicitTransition( commandBuffer , index , view ); if ( !attach.isNoTransition() && ( attach.isStorageBuffer() || attach.isTransferBuffer() || attach.isTransitionBuffer() ) ) { auto buffer = view.data->buffer; auto & range = getSubresourceRange( view ); auto currentState = recordContext.getAccessState( view ); if ( attach.isClearableBuffer() ) { recordContext.memoryBarrier( commandBuffer , buffer , range , currentState , { AccessFlags::eTransferWrite, PipelineStageFlags::eTransfer } ); recordContext->vkCmdFillBuffer( commandBuffer , graph.createBuffer( buffer ) , range.offset == 0u ? 0u : details::getAlignedSize( range.offset, 4u ) , range.size == VK_WHOLE_SIZE ? VK_WHOLE_SIZE : details::getAlignedSize( range.size, 4u ) , 0u ); currentState.access = AccessFlags::eTransferWrite; currentState.pipelineStage = PipelineStageFlags::eTransfer; } recordContext.memoryBarrier( commandBuffer , buffer , range , currentState , { attach.getAccessMask(), attach.getPipelineStageFlags( isComputePass ) } ); } } static void prepareResources( VkCommandBuffer commandBuffer , uint32_t index , RecordContext & recordContext , RunnableGraph & graph , FramePass const & pass , RunnablePass::Callbacks const & callbacks , GraphContext const & graphContext ) { for ( auto & [binding, attach] : pass.getSampled() ) prepareImage( commandBuffer, index, *attach.attach, graphContext.separateDepthStencilLayouts, graph, recordContext ); for ( auto & [binding, attach] : pass.getUniforms() ) prepareBuffer( commandBuffer, index, *attach, callbacks.isComputePass(), graph, recordContext ); for ( auto & [binding, attach] : pass.getInputs() ) if ( attach->isImage() ) prepareImage( commandBuffer, index, *attach, graphContext.separateDepthStencilLayouts, graph, recordContext ); else prepareBuffer( commandBuffer, index, *attach, callbacks.isComputePass(), graph, recordContext ); for ( auto & [binding, attach] : pass.getInouts() ) if ( attach->isImage() ) prepareImage( commandBuffer, index, *attach, graphContext.separateDepthStencilLayouts, graph, recordContext ); else prepareBuffer( commandBuffer, index, *attach, callbacks.isComputePass(), graph, recordContext ); for ( auto & [binding, attach] : pass.getOutputs() ) if ( attach->isImage() ) prepareImage( commandBuffer, index, *attach, graphContext.separateDepthStencilLayouts, graph, recordContext ); else prepareBuffer( commandBuffer, index, *attach, callbacks.isComputePass(), graph, recordContext ); for ( auto & attach : pass.getTargets() ) prepareImage( commandBuffer, index, *attach, graphContext.separateDepthStencilLayouts, graph, recordContext ); } } //********************************************************************************************* void checkUndefinedInput( std::string const & stepName , Attachment const & attach , ImageViewId const & view , ImageLayout currentLayout ) { if ( !attach.isTransitionImageView() && attach.isInput() && currentLayout == ImageLayout::eUndefined ) { auto passName = attach.pass ? attach.pass->getFullName() : attach.source.front().pass->getFullName(); Logger::logWarning( stepName + " - [" + passName + "]: Input view [" + view.data->name + "] is currently in undefined layout" ); } } void convert( SemaphoreWaitArray const & toWait , std::vector< VkSemaphore > & semaphores , std::vector< VkPipelineStageFlags > & dstStageMasks ) { for ( auto & wait : toWait ) { if ( wait.semaphore && semaphores.end() == std::find( semaphores.begin() , semaphores.end() , wait.semaphore ) ) { semaphores.push_back( wait.semaphore ); dstStageMasks.push_back( getPipelineStageFlags( wait.dstStageMask ) ); } } } //********************************************************************************************* RunnablePass::Callbacks::Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState ) : Callbacks{ std::move( initialise ) , std::move( getPipelineState ) , getDefaultV< RecordCallback >() , getDefaultV< GetPassIndexCallback >() , getDefaultV< IsEnabledCallback >() , getDefaultV< IsComputePassCallback >() } { } RunnablePass::Callbacks::Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState , RecordCallback record ) : Callbacks{ std::move( initialise ) , std::move( getPipelineState ) , std::move( record ) , getDefaultV< GetPassIndexCallback >() , getDefaultV< IsEnabledCallback >() , getDefaultV< IsComputePassCallback >() } { } RunnablePass::Callbacks::Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState , RecordCallback record , GetPassIndexCallback getPassIndex ) : Callbacks{ std::move( initialise ) , std::move( getPipelineState ) , std::move( record ) , std::move( getPassIndex ) , getDefaultV< IsEnabledCallback >() , getDefaultV< IsComputePassCallback >() } { } RunnablePass::Callbacks::Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState , RecordCallback record , GetPassIndexCallback getPassIndex , IsEnabledCallback isEnabled ) : Callbacks{ std::move( initialise ) , std::move( getPipelineState ) , std::move( record ) , std::move( getPassIndex ) , std::move( isEnabled ) , getDefaultV< IsComputePassCallback >() } { } RunnablePass::Callbacks::Callbacks( InitialiseCallback initialise , GetPipelineStateCallback getPipelineState , RecordCallback record , GetPassIndexCallback getPassIndex , IsEnabledCallback isEnabled , IsComputePassCallback isComputePass ) : initialise{ std::move( initialise ) } , getPipelineState{ std::move( getPipelineState ) } , record{ std::move( record ) } , getPassIndex{ std::move( getPassIndex ) } , isEnabled{ std::move( isEnabled ) } , isComputePass{ std::move( isComputePass ) } { } //********************************************************************************************* Fence::Fence( GraphContext & context , std::string const & name , VkFenceCreateInfo createInfo ) : m_context{ &context } { if ( m_context->vkCreateFence ) { auto res = m_context->vkCreateFence( m_context->device , &createInfo , m_context->allocator , &m_fence ); checkVkResult( res, name + " - Fence creation" ); crgRegisterObject( *m_context, name, m_fence ); } } Fence::Fence( Fence && rhs )noexcept : m_context{ rhs.m_context } , m_fence{ rhs.m_fence } , m_fenceWaited{ rhs.m_fenceWaited } { rhs.m_context = {}; rhs.m_fence = {}; } Fence::~Fence()noexcept { if ( m_fence && m_context && m_context->vkDestroyFence ) { crgUnregisterObject( *m_context, m_fence ); m_context->vkDestroyFence( m_context->device , m_fence , m_context->allocator ); } } void Fence::reset() { if ( m_fence ) { if ( !m_fenceWaited ) { wait( 0xFFFFFFFFFFFFFFFFULL ); } m_context->vkResetFences( m_context->device , 1u , &m_fence ); } m_fenceWaited = false; } VkResult Fence::wait( uint64_t timeout ) { VkResult res = VK_SUCCESS; if ( m_fence ) { res = m_context->vkWaitForFences( m_context->device , 1u , &m_fence , VK_TRUE , timeout ); } m_fenceWaited = true; return res; } //********************************************************************************************* RunnablePass::PassData::PassData( RunnableGraph & grh , GraphContext & ctx , std::string const & baseName ) : graph{ grh } , context{ ctx } , fence{ context, baseName, { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT } } { if ( context.vkCreateSemaphore ) { VkSemaphoreCreateInfo createInfo{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO , nullptr , 0u }; auto res = context.vkCreateSemaphore( context.device , &createInfo , context.allocator , &semaphore ); checkVkResult( res, baseName + " - Semaphore creation" ); crgRegisterObject( context, baseName, semaphore ); } } RunnablePass::PassData::~PassData()noexcept { if ( context.vkDestroySemaphore && semaphore ) { crgUnregisterObject( context, semaphore ); context.vkDestroySemaphore( context.device , semaphore , context.allocator ); } if ( context.vkFreeCommandBuffers && commandBuffer.commandBuffer ) { crgUnregisterObject( context, commandBuffer.commandBuffer ); context.vkFreeCommandBuffers( context.device , graph.getCommandPool() , 1u , &commandBuffer.commandBuffer ); } } //********************************************************************************************* RunnablePass::RunnablePass( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Callbacks callbacks , ru::Config ruConfig ) : m_pass{ pass } , m_context{ context } , m_graph{ graph } , m_callbacks{ std::move( callbacks ) } , m_ruConfig{ std::move( ruConfig ) } , m_pipelineState{ m_callbacks.getPipelineState() } , m_timer{ context, pass.getGroupName(), TimerScope::ePass, graph.getTimerQueryPool(), graph.getTimerQueryOffset() } { for ( uint32_t i = 0u; i < m_ruConfig.maxPassCount; ++i ) { m_passes.emplace_back( m_graph, m_context, m_pass.getGroupName() ); m_passContexts.emplace_back( graph.getResources() ); } details::registerResources( pass, m_callbacks, m_context , m_imageLayouts, m_bufferAccesses ); } RunnablePass::~RunnablePass()noexcept { m_bufferAccesses.clear(); m_passContexts.clear(); m_passes.clear(); } void RunnablePass::initialise( uint32_t passIndex ) { assert( m_passes.size() > passIndex ); auto & pass = m_passes[passIndex]; m_callbacks.initialise( passIndex ); pass.initialised = true; } uint32_t RunnablePass::recordCurrentInto( RecordContext & context , VkCommandBuffer commandBuffer ) { auto index = m_callbacks.getPassIndex(); recordInto( commandBuffer , index , context ); return isEnabled() ? index : InvalidIndex; } uint32_t RunnablePass::reRecordCurrent() { assert( m_ruConfig.resettable ); auto index = m_callbacks.getPassIndex(); if ( index < m_passContexts.size() ) { auto context = m_passContexts[index]; recordOne( m_passes[index].commandBuffer , index , context ); } return isEnabled() ? index : InvalidIndex; } void RunnablePass::recordOne( CommandBuffer & enabled , uint32_t index , RecordContext & context ) { if ( !enabled.commandBuffer ) { enabled.commandBuffer = doCreateCommandBuffer( std::string{} ); } assert( m_passes.size() > index ); auto & pass = m_passes[index]; pass.fence.wait( 0xFFFFFFFFFFFFFFFFULL ); VkCommandBufferBeginInfo beginInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO , nullptr , VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT , nullptr }; m_context.vkBeginCommandBuffer( enabled.commandBuffer, &beginInfo ); recordInto( enabled.commandBuffer, index, context ); m_context.vkEndCommandBuffer( enabled.commandBuffer ); } void RunnablePass::recordInto( VkCommandBuffer commandBuffer , uint32_t index , RecordContext & context ) { if ( m_ruConfig.resettable ) { m_passContexts[index] = context; } if ( isEnabled() ) { assert( m_passes.size() > index ); if ( auto const & pass = m_passes[index]; !pass.initialised ) { initialise( index ); } auto block( m_timer.start() ); m_timer.beginPass( commandBuffer, m_pass.getGroupName(), m_pass.getId() ); details::prepareResources( commandBuffer, index, context , m_graph, m_pass, m_callbacks, m_context ); for ( auto const & action : m_ruConfig.prePassActions ) { action( context, commandBuffer, index ); } m_callbacks.record( context, commandBuffer, index ); for ( auto const & action : m_ruConfig.postPassActions ) { action( context, commandBuffer, index ); } m_timer.endPass( commandBuffer ); } for ( auto const & [view, action] : m_ruConfig.implicitImageActions ) { context.registerImplicitTransition( *this, view, action ); } for ( auto const & [view, action] : m_ruConfig.implicitBufferActions ) { context.registerImplicitTransition( *this, view, action ); } } bool RunnablePass::resetCommandBuffer( uint32_t passIndex ) { bool result{}; if ( m_context.device ) { assert( m_passes.size() > passIndex ); auto & pass = m_passes[passIndex]; if ( pass.commandBuffer.commandBuffer ) { result = true; pass.fence.wait( 0xFFFFFFFFFFFFFFFFULL ); pass.commandBuffer.recorded = false; m_context.vkResetCommandBuffer( pass.commandBuffer.commandBuffer , VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT ); } } return result; } void RunnablePass::notifyPassRender() { if ( isEnabled() ) { m_timer.notifyPassRender( getIndex() ); } } VkCommandBuffer RunnablePass::doCreateCommandBuffer( std::string const & suffix ) { VkCommandBuffer result{}; if ( m_context.vkAllocateCommandBuffers ) { VkCommandBufferAllocateInfo allocateInfo{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO , nullptr , m_graph.getCommandPool() , VK_COMMAND_BUFFER_LEVEL_PRIMARY , 1u }; auto res = m_context.vkAllocateCommandBuffers( m_context.device , &allocateInfo , &result ); checkVkResult( res, m_pass.getGroupName() + suffix + " - CommandBuffer allocation" ); crgRegisterObject( m_context, m_pass.getGroupName() + suffix, result ); } return result; } } ================================================ FILE: source/RenderGraph/RunnablePasses/BufferCopy.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/BufferCopy.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/RunnableGraph.hpp" #include namespace crg { BufferCopy::BufferCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , DeviceSize copyOffset , DeviceSize copyRange , ru::Config ruConfig , GetPassIndexCallback passIndex , IsEnabledCallback isEnabled ) : RunnablePass{ pass , context , graph , { defaultV< InitialiseCallback > , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eTransfer ); } ) , [this]( RecordContext & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , std::move( passIndex ) , std::move( isEnabled ) } , std::move( ruConfig ) } , m_copyOffset{ copyOffset } , m_copyRange{ copyRange } { assert( getPass().getInputs().size() == getPass().getOutputs().size() ); } void BufferCopy::doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const { auto srcIt = getPass().getInputs().begin(); auto dstIt = getPass().getOutputs().begin(); while ( srcIt != getPass().getInputs().end() && dstIt != getPass().getOutputs().end() ) { auto srcView{ srcIt->second->buffer( index ) }; auto dstView{ dstIt->second->buffer( index ) }; auto srcBufferRange{ getSubresourceRange( srcView ) }; auto dstBufferRange{ getSubresourceRange( dstView ) }; auto srcBuffer{ getGraph().createBuffer( srcView.data->buffer ) }; auto dstBuffer{ getGraph().createBuffer( dstView.data->buffer ) }; // Copy source to target. VkBufferCopy copyRegion{ srcBufferRange.offset + m_copyOffset , dstBufferRange.offset + m_copyOffset , m_copyRange }; context.memoryBarrier( commandBuffer , srcView , { AccessFlags::eShaderWrite, PipelineStageFlags::eFragmentShader } , { AccessFlags::eTransferRead, PipelineStageFlags::eTransfer } ); context.memoryBarrier( commandBuffer , dstView , { AccessFlags::eTransferWrite, PipelineStageFlags::eTransfer } ); context->vkCmdCopyBuffer( commandBuffer , srcBuffer , dstBuffer , 1u , ©Region ); context.memoryBarrier( commandBuffer , dstView , { AccessFlags::eShaderRead, PipelineStageFlags::eComputeShader } ); context.memoryBarrier( commandBuffer , srcView , { AccessFlags::eShaderWrite, PipelineStageFlags::eFragmentShader } ); ++srcIt; ++dstIt; } } } ================================================ FILE: source/RenderGraph/RunnablePasses/BufferToImageCopy.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/BufferToImageCopy.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/RunnableGraph.hpp" #include namespace crg { BufferToImageCopy::BufferToImageCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Offset3D const & copyOffset , Extent3D const & copySize , ru::Config ruConfig , GetPassIndexCallback passIndex , IsEnabledCallback isEnabled ) : RunnablePass{ pass , context , graph , { defaultV< InitialiseCallback > , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eTransfer ); } ) , [this]( RecordContext const & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , std::move( passIndex ) , std::move( isEnabled ) } , std::move( ruConfig ) } , m_copyOffset{ convert( copyOffset ) } , m_copySize{ convert( copySize ) } { assert( getPass().getInputs().size() == getPass().getOutputs().size() ); } void BufferToImageCopy::doRecordInto( RecordContext const & context , VkCommandBuffer commandBuffer , uint32_t index )const { auto srcIt = getPass().getInputs().begin(); auto dstIt = getPass().getOutputs().begin(); while ( srcIt != getPass().getInputs().end() && dstIt != getPass().getOutputs().end() ) { auto srcAttach{ srcIt->second->buffer( index ) }; auto dstAttach{ dstIt->second->view( index ) }; auto srcBuffer{ getGraph().createBuffer( srcAttach.data->buffer ) }; auto dstImage{ getGraph().createImage( dstAttach.data->image ) }; // Copy source to target. auto range = getSubresourceLayers( getSubresourceRange( dstAttach ) ); VkBufferImageCopy copyRegion{ 0ULL , 0u , 0u , range , m_copyOffset , m_copySize }; context->vkCmdCopyBufferToImage( commandBuffer , srcBuffer , dstImage , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1u , ©Region ); ++srcIt; ++dstIt; } } } ================================================ FILE: source/RenderGraph/RunnablePasses/ComputePass.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/ComputePass.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/RunnableGraph.hpp" #include namespace crg { namespace cppss { static bool isPtrEnabled( bool const * v ) { return v ? *v : true; } } ComputePass::ComputePass( FramePass const & pass , GraphContext & context , RunnableGraph & graph , ru::Config const & ruConfig , cp::Config cpConfig ) : RunnablePass{ pass , context , graph , { [this]( uint32_t index ){ doInitialise( index ); } , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eComputeShader ); } ) , [this]( RecordContext & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , GetPassIndexCallback( [this](){ return doGetPassIndex(); } ) , IsEnabledCallback( [this](){ return doIsEnabled(); } ) , IsComputePassCallback( [](){ return true; } ) } , ruConfig } , m_cpConfig{ cpConfig.m_initialise ? std::move( *cpConfig.m_initialise ) : getDefaultV< RunnablePass::InitialiseCallback >() , cpConfig.m_enabled.has_value() ? std::move( *cpConfig.m_enabled ) : getDefaultV< bool const * >() , cpConfig.m_isEnabled , cpConfig.m_getPassIndex ? std::move( *cpConfig.m_getPassIndex ) : getDefaultV< RunnablePass::GetPassIndexCallback >() , cpConfig.m_recordInto ? std::move( *cpConfig.m_recordInto ) : getDefaultV< RunnablePass::RecordCallback >() , cpConfig.m_end ? std::move( *cpConfig.m_end ) : getDefaultV< RunnablePass::RecordCallback >() , cpConfig.m_groupCountX.has_value() ? *cpConfig.m_groupCountX : 1u , cpConfig.m_groupCountY.has_value() ? *cpConfig.m_groupCountY : 1u , cpConfig.m_groupCountZ.has_value() ? *cpConfig.m_groupCountZ : 1u , cpConfig.m_getGroupCountX ? std::optional< cp::GetGroupCountCallback >( std::move( *cpConfig.m_getGroupCountX ) ) : std::nullopt , cpConfig.m_getGroupCountY ? std::optional< cp::GetGroupCountCallback >( std::move( *cpConfig.m_getGroupCountY ) ) : std::nullopt , cpConfig.m_getGroupCountZ ? std::optional< cp::GetGroupCountCallback >( std::move( *cpConfig.m_getGroupCountZ ) ) : std::nullopt , cpConfig.m_indirectBuffer ? *cpConfig.m_indirectBuffer : getDefaultV < IndirectBuffer >() } , m_pipeline{ pass , context , graph , std::move( cpConfig.m_baseConfig ) , VK_PIPELINE_BIND_POINT_COMPUTE , ruConfig.maxPassCount } { } void ComputePass::resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ) { resetCommandBuffer( index ); m_pipeline.resetPipeline( std::move( config ), index ); doCreatePipeline( index ); reRecordCurrent(); } VkPipelineLayout ComputePass::getPipelineLayout()const { return m_pipeline.getPipelineLayout(); } void ComputePass::doInitialise( uint32_t index ) { m_pipeline.initialise(); doCreatePipeline( index ); m_cpConfig.initialise( index ); } uint32_t ComputePass::doGetPassIndex()const { return m_cpConfig.getPassIndex(); } bool ComputePass::doIsEnabled()const { return ( m_cpConfig.isEnabled ? ( *m_cpConfig.isEnabled )() : cppss::isPtrEnabled( m_cpConfig.enabled ) ); } void ComputePass::doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ) { m_pipeline.recordInto( context, commandBuffer, index ); m_cpConfig.recordInto( context, commandBuffer, index ); if ( m_cpConfig.indirectBuffer != defaultV< IndirectBuffer > ) { auto indirectBuffer = getGraph().createBuffer( m_cpConfig.indirectBuffer.buffer.data->buffer ); context->vkCmdDispatchIndirect( commandBuffer, indirectBuffer, getSubresourceRange( m_cpConfig.indirectBuffer.buffer ).offset ); } else { context->vkCmdDispatch( commandBuffer , ( m_cpConfig.getGroupCountX ? ( *m_cpConfig.getGroupCountX )() : m_cpConfig.groupCountX ) , ( m_cpConfig.getGroupCountY ? ( *m_cpConfig.getGroupCountY )() : m_cpConfig.groupCountY ) , ( m_cpConfig.getGroupCountZ ? ( *m_cpConfig.getGroupCountZ )() : m_cpConfig.groupCountZ ) ); } m_cpConfig.end( context, commandBuffer, index ); } void ComputePass::doCreatePipeline( uint32_t index ) { auto & program = m_pipeline.getProgram( index ); VkComputePipelineCreateInfo createInfo{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO , nullptr , 0u , program.front() , getPipelineLayout() , VkPipeline{} , 0u }; m_pipeline.createPipeline( index, createInfo ); } } ================================================ FILE: source/RenderGraph/RunnablePasses/GenerateMipmaps.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/GenerateMipmaps.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/RunnableGraph.hpp" #include namespace crg { namespace genMips { template< typename T > static constexpr T getSubresourceDimension( T const & extent , uint32_t mipLevel )noexcept { return std::max( T( 1 ), T( extent >> mipLevel ) ); } } GenerateMipmaps::GenerateMipmaps( FramePass const & pass , GraphContext & context , RunnableGraph & graph , ImageLayout outputLayout , ru::Config ruConfig , GetPassIndexCallback passIndex , IsEnabledCallback isEnabled ) : RunnablePass{ pass , context , graph , { defaultV< InitialiseCallback > , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eTransfer ); } ) , [this]( RecordContext & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , std::move( passIndex ) , std::move( isEnabled ) } , std::move( ruConfig ) } , m_outputLayout{ makeLayoutState( outputLayout ) } { } void GenerateMipmaps::doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const { for ( auto const & [_, view] : getPass().getInouts() ) doProcessImageView( context, commandBuffer, view->view( index ) ); } void GenerateMipmaps::doProcessImageView( RecordContext & context , VkCommandBuffer commandBuffer , ImageViewId viewId )const { auto imageId{ viewId.data->image }; auto image{ getGraph().createImage( imageId ) }; auto extent = getExtent( imageId ); auto format = getFormat( imageId ); auto baseArrayLayer = getSubresourceRange( viewId ).baseArrayLayer; auto layerCount = getSubresourceRange( viewId ).layerCount; auto mipLevels = imageId.data->info.mipLevels; auto nextLayoutState = getGraph().getNextLayoutState( context , *this , viewId ); auto const width = int32_t( extent.width ); auto const height = int32_t( extent.height ); auto const depth = int32_t( extent.depth ); auto const aspectMask = getAspectMask( format ); for ( uint32_t i = 0u; i < layerCount; ++i ) { auto layer = baseArrayLayer + i; ImageSubresourceRange mipSubRange{ aspectMask , 0u , 1u , layer , 1u }; VkImageBlit imageBlit{}; imageBlit.dstSubresource.aspectMask = getImageAspectFlags( aspectMask ); imageBlit.dstSubresource.baseArrayLayer = mipSubRange.baseArrayLayer; imageBlit.dstSubresource.layerCount = 1; imageBlit.dstSubresource.mipLevel = mipSubRange.baseMipLevel; imageBlit.dstOffsets[0].x = 0; imageBlit.dstOffsets[0].y = 0; imageBlit.dstOffsets[0].z = 0; imageBlit.dstOffsets[1].x = genMips::getSubresourceDimension( width, mipSubRange.baseMipLevel ); imageBlit.dstOffsets[1].y = genMips::getSubresourceDimension( height, mipSubRange.baseMipLevel ); imageBlit.dstOffsets[1].z = genMips::getSubresourceDimension( depth, mipSubRange.baseMipLevel ); // Transition first mip level to transfer source for read in next iteration auto firstLayoutState = getGraph().getCurrentLayoutState( context , imageId , viewId.data->info.viewType , mipSubRange ); context.memoryBarrier( commandBuffer , imageId , mipSubRange , firstLayoutState.layout , makeLayoutState( ImageLayout::eTransferSrc ) ); // Copy down mips while ( ++mipSubRange.baseMipLevel < mipLevels ) { // Blit from previous level // Blit source is previous blit destination imageBlit.srcSubresource = imageBlit.dstSubresource; imageBlit.srcOffsets[0] = imageBlit.dstOffsets[0]; imageBlit.srcOffsets[1] = imageBlit.dstOffsets[1]; // Update blit destination imageBlit.dstSubresource.mipLevel = mipSubRange.baseMipLevel; imageBlit.dstOffsets[1].x = genMips::getSubresourceDimension( width, mipSubRange.baseMipLevel ); imageBlit.dstOffsets[1].y = genMips::getSubresourceDimension( height, mipSubRange.baseMipLevel ); imageBlit.dstOffsets[1].z = genMips::getSubresourceDimension( depth, mipSubRange.baseMipLevel ); // Transition current mip level to transfer dest context.memoryBarrier( commandBuffer , imageId , mipSubRange , makeLayoutState( ImageLayout::eTransferDst ) ); // Perform blit context->vkCmdBlitImage( commandBuffer , image , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , image , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1u , &imageBlit , VK_FILTER_LINEAR ); // Transition previous mip level to wanted output layout context.memoryBarrier( commandBuffer , imageId , { mipSubRange.aspectMask , mipSubRange.baseMipLevel - 1u , 1u , mipSubRange.baseArrayLayer , 1u } , ImageLayout::eTransferSrc , nextLayoutState ); if ( mipSubRange.baseMipLevel == ( mipLevels - 1u ) ) { // Transition final mip level to wanted output layout context.memoryBarrier( commandBuffer , imageId , mipSubRange , ImageLayout::eTransferDst , nextLayoutState ); } else { // Transition current mip level to transfer source for read in next iteration context.memoryBarrier( commandBuffer , imageId , mipSubRange , ImageLayout::eTransferDst , makeLayoutState( ImageLayout::eTransferSrc ) ); } } } } } ================================================ FILE: source/RenderGraph/RunnablePasses/ImageBlit.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/ImageBlit.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/RunnableGraph.hpp" #include namespace crg { ImageBlit::ImageBlit( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Rect3D const & blitSrc , Rect3D const & blitDst , FilterMode filter , ru::Config ruConfig , GetPassIndexCallback passIndex , IsEnabledCallback isEnabled ) : RunnablePass{ pass , context , graph , { defaultV< InitialiseCallback > , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eTransfer ); } ) , [this]( RecordContext const & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , std::move( passIndex ) , std::move( isEnabled ) } , std::move( ruConfig ) } , m_srcOffset{ convert( blitSrc.offset ) } , m_srcSize{ convert( blitSrc.extent ) } , m_dstOffset{ convert( blitDst.offset ) } , m_dstSize{ convert( blitDst.extent ) } , m_filter{ filter } { assert( getPass().getInputs().size() == getPass().getOutputs().size() ); } void ImageBlit::doRecordInto( RecordContext const & context , VkCommandBuffer commandBuffer , uint32_t index )const { auto srcIt = getPass().getInputs().begin(); auto dstIt = getPass().getOutputs().begin(); while ( srcIt != getPass().getInputs().end() && dstIt != getPass().getOutputs().end() ) { auto srcAttach{ srcIt->second->view( index ) }; auto dstAttach{ dstIt->second->view( index ) }; auto srcImage{ getGraph().createImage( srcAttach.data->image ) }; auto dstImage{ getGraph().createImage( dstAttach.data->image ) }; VkImageBlit blitRegion{ getSubresourceLayers( getSubresourceRange( srcAttach ) ) , { m_srcOffset, VkOffset3D{ int32_t( m_srcSize.width ), int32_t( m_srcSize.height ), int32_t( m_srcSize.depth ) } } , getSubresourceLayers( getSubresourceRange( dstAttach ) ) , { m_dstOffset, VkOffset3D{ int32_t( m_dstSize.width ), int32_t( m_dstSize.height ), int32_t( m_dstSize.depth ) } } }; context->vkCmdBlitImage( commandBuffer , srcImage , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , dstImage , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1u , &blitRegion , convert( m_filter ) ); ++srcIt; ++dstIt; } } } ================================================ FILE: source/RenderGraph/RunnablePasses/ImageCopy.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/ImageCopy.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/RunnableGraph.hpp" #include namespace crg { ImageCopy::ImageCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Extent3D const & copySize , ImageLayout finalOutputLayout , ru::Config ruConfig , GetPassIndexCallback passIndex , IsEnabledCallback isEnabled ) : RunnablePass{ pass , context , graph , { defaultV< InitialiseCallback > , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eTransfer ); } ) , [this]( RecordContext & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , std::move( passIndex ) , std::move( isEnabled ) } , std::move( ruConfig ) } , m_copySize{ convert( copySize ) } , m_finalOutputLayout{ finalOutputLayout } { assert( getPass().getInputs().size() == getPass().getOutputs().size() || getPass().getInputs().size() == 1u || getPass().getOutputs().size() == 1u ); } ImageCopy::ImageCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Extent3D const & copySize , ru::Config ruConfig , GetPassIndexCallback passIndex , IsEnabledCallback isEnabled ) : ImageCopy{ pass , context , graph , copySize , ImageLayout::eUndefined , std::move( ruConfig ) , std::move( passIndex ) , std::move( isEnabled ) } { assert( getPass().getInputs().size() == getPass().getOutputs().size() || getPass().getInputs().size() == 1u || getPass().getOutputs().size() == 1u ); } void ImageCopy::doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const { if ( getPass().getInputs().size() == getPass().getOutputs().size() ) doRecordMultiToMulti( context, commandBuffer, index ); else if ( getPass().getOutputs().size() == 1u ) doRecordMultiToSingle( context, commandBuffer, index ); else if ( getPass().getInputs().size() == 1u ) doRecordSingleToMulti( context, commandBuffer, index ); } void ImageCopy::doRecordMultiToMulti( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const { auto srcIt = getPass().getInputs().begin(); auto dstIt = getPass().getOutputs().begin(); while ( srcIt != getPass().getInputs().end() && dstIt != getPass().getOutputs().end() ) { auto srcAttach{ srcIt->second->view( index ) }; auto dstAttach{ dstIt->second->view( index ) }; auto srcImage{ getGraph().createImage( srcAttach.data->image ) }; auto dstImage{ getGraph().createImage( dstAttach.data->image ) }; // Copy source to target. VkImageCopy copyRegion{ getSubresourceLayers( getSubresourceRange( srcAttach ) ) , {} , getSubresourceLayers( getSubresourceRange( dstAttach ) ) , {} , m_copySize }; context->vkCmdCopyImage( commandBuffer , srcImage , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , dstImage , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , 1u , ©Region ); if ( m_finalOutputLayout != ImageLayout::eUndefined ) { context.memoryBarrier( commandBuffer , dstAttach , crg::makeLayoutState( m_finalOutputLayout ) ); } ++srcIt; ++dstIt; } } void ImageCopy::doRecordMultiToSingle( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const { std::vector< VkImageCopy > copyRegions; auto dstIt = getPass().getOutputs().begin(); auto dstAttach{ dstIt->second->view( index ) }; auto dstImage{ getGraph().createImage( dstAttach.data->image ) }; auto dstSubresourceRange = getSubresourceLayers( getSubresourceRange( dstAttach ) ); auto prvSrcImage{ getGraph().createImage( getPass().getInputs().begin()->second->view( index ).data->image ) }; for ( auto const & [_, attach] : getPass().getInputs() ) { auto srcAttach{ attach->view( index ) }; if ( auto srcImage{ getGraph().createImage( srcAttach.data->image ) }; srcImage != prvSrcImage ) { context->vkCmdCopyImage( commandBuffer , prvSrcImage , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , dstImage , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , uint32_t( copyRegions.size() ) , copyRegions.data() ); copyRegions.clear(); prvSrcImage = srcImage; } copyRegions.push_back( { getSubresourceLayers( getSubresourceRange( srcAttach ) ) , {} , dstSubresourceRange , {} , m_copySize } ); } if ( !copyRegions.empty() ) { context->vkCmdCopyImage( commandBuffer , prvSrcImage , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , dstImage , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , uint32_t( copyRegions.size() ) , copyRegions.data() ); } if ( m_finalOutputLayout != ImageLayout::eUndefined ) { context.memoryBarrier( commandBuffer , dstAttach , crg::makeLayoutState( m_finalOutputLayout ) ); } } void ImageCopy::doRecordSingleToMulti( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const { std::vector< VkImageCopy > copyRegions; auto srcIt = getPass().getInputs().begin(); auto srcAttach{ srcIt->second->view( index ) }; auto srcImage{ getGraph().createImage( srcAttach.data->image ) }; auto srcSubresourceRange = getSubresourceLayers( getSubresourceRange( srcAttach ) ); auto prvDstImage{ getGraph().createImage( getPass().getOutputs().begin()->second->view( index ).data->image ) }; for ( auto const & [_, attach] : getPass().getOutputs() ) { auto dstAttach{ attach->view( index ) }; if ( auto dstImage{ getGraph().createImage( dstAttach.data->image ) }; dstImage != prvDstImage ) { context->vkCmdCopyImage( commandBuffer , srcImage , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , prvDstImage , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , uint32_t( copyRegions.size() ) , copyRegions.data() ); copyRegions.clear(); prvDstImage = dstImage; } copyRegions.push_back( { srcSubresourceRange , {} , getSubresourceLayers( getSubresourceRange( dstAttach ) ) , {} , m_copySize } ); } if ( !copyRegions.empty() ) { context->vkCmdCopyImage( commandBuffer , srcImage , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , prvDstImage , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , uint32_t( copyRegions.size() ) , copyRegions.data() ); } if ( m_finalOutputLayout != ImageLayout::eUndefined ) { for ( auto const & [_, attach] : getPass().getOutputs() ) { auto dstAttach{ attach->view( index ) }; context.memoryBarrier( commandBuffer , dstAttach , crg::makeLayoutState( m_finalOutputLayout ) ); } } } } ================================================ FILE: source/RenderGraph/RunnablePasses/ImageToBufferCopy.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/ImageToBufferCopy.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/RunnableGraph.hpp" #include namespace crg { ImageToBufferCopy::ImageToBufferCopy( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Offset3D const & copyOffset , Extent3D const & copySize , ru::Config ruConfig , GetPassIndexCallback passIndex , IsEnabledCallback isEnabled ) : RunnablePass{ pass , context , graph , { defaultV< InitialiseCallback > , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eTransfer ); } ) , [this]( RecordContext const & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , std::move( passIndex ) , std::move( isEnabled ) } , std::move( ruConfig ) } , m_copyOffset{ convert( copyOffset ) } , m_copySize{ convert( copySize ) } { } void ImageToBufferCopy::doRecordInto( RecordContext const & context , VkCommandBuffer commandBuffer , uint32_t index )const { auto srcIt = getPass().getInputs().begin(); auto dstIt = getPass().getOutputs().begin(); while ( srcIt != getPass().getInputs().end() && dstIt != getPass().getOutputs().end() ) { auto srcAttach{ srcIt->second->view( index ) }; auto dstAttach{ dstIt->second->buffer( index ) }; auto srcImage{ getGraph().createImage( srcAttach.data->image ) }; auto dstBuffer{ getGraph().createBuffer( dstAttach.data->buffer ) }; // Copy source to target. auto range = getSubresourceLayers( getSubresourceRange( srcAttach ) ); VkBufferImageCopy copyRegion{ 0ULL , 0u , 0u , range , m_copyOffset , m_copySize }; context->vkCmdCopyImageToBuffer( commandBuffer , srcImage , VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL , dstBuffer , 1u , ©Region ); ++srcIt; ++dstIt; } } } ================================================ FILE: source/RenderGraph/RunnablePasses/PipelineHolder.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/PipelineHolder.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/RunnableGraph.hpp" #include "RenderGraph/RunnablePasses/RenderPass.hpp" #include namespace crg { namespace pphdr { static bool isDescriptor( Attachment const & attach ) { return attach.isStorageImageView() || attach.isSampledImageView() || attach.isUniformBuffer() || attach.isStorageBuffer() || attach.isUniformBufferView() || attach.isStorageBufferView(); } static void createDescriptorWrites( std::map< uint32_t, FramePass::SampledAttachment > const & attaches , uint32_t index , RunnableGraph & graph , WriteDescriptorSetArray & writes ) { for ( auto & [binding, attach] : attaches ) writes.push_back( graph.getDescriptorWrite( *attach.attach, attach.sampler, binding, index ) ); } static void createDescriptorWrites( std::map< uint32_t, Attachment const * > const & attaches , uint32_t index , RunnableGraph & graph , WriteDescriptorSetArray & writes ) { for ( auto & [binding, attach] : attaches ) { if ( isDescriptor( *attach ) ) writes.push_back( graph.getDescriptorWrite( *attach, binding, index ) ); } } static void createDescriptorBindings( std::map< uint32_t, FramePass::SampledAttachment > const & attaches , VkShaderStageFlags shaderStage , RunnableGraph const & graph , VkDescriptorSetLayoutBindingArray & descriptorBindings ) { for ( auto & [binding, attach] : attaches ) { descriptorBindings.push_back( { binding , graph.getDescriptorType( *attach.attach ) , 1u, shaderStage, nullptr } ); } } static void createDescriptorBindings( std::map< uint32_t, Attachment const * > const & attaches , VkShaderStageFlags shaderStage , RunnableGraph const & graph , VkDescriptorSetLayoutBindingArray & descriptorBindings ) { for ( auto & [binding, attach] : attaches ) { if ( isDescriptor( *attach ) ) descriptorBindings.push_back( { binding , graph.getDescriptorType( *attach ) , 1u, shaderStage, nullptr } ); } } } PipelineHolder::PipelineHolder( FramePass const & pass , GraphContext & context , RunnableGraph & graph , pp::Config config , VkPipelineBindPoint bindingPoint , uint32_t maxPassCount ) : m_pass{ pass } , m_context{ context } , m_graph{ graph } , m_baseConfig{ config.m_programs ? *config.m_programs : defaultV< std::vector< VkPipelineShaderStageCreateInfoArray > > , config.m_programCreator ? *config.m_programCreator : defaultV< ProgramCreator > , config.m_layouts ? *config.m_layouts : defaultV< std::vector< VkDescriptorSetLayout > > , config.m_pushConstants ? *config.m_pushConstants : defaultV< std::vector< VkPushConstantRange > > } , m_bindingPoint{ bindingPoint } { if ( m_baseConfig.m_programCreator.create ) { m_pipelines.resize( m_baseConfig.m_programCreator.maxCount, VkPipeline{} ); m_baseConfig.m_programs.resize( m_baseConfig.m_programCreator.maxCount ); } else { m_pipelines.resize( m_baseConfig.m_programs.size(), VkPipeline{} ); } m_descriptorSets.resize( maxPassCount ); } PipelineHolder::~PipelineHolder()noexcept { cleanup(); } void PipelineHolder::initialise() { if ( !m_pipelineLayout ) { doFillDescriptorBindings(); doCreateDescriptorSetLayout(); doCreatePipelineLayout(); doCreateDescriptorPool(); } } void PipelineHolder::cleanup()noexcept { m_descriptorBindings.clear(); for ( auto & descriptorSet : m_descriptorSets ) { if ( descriptorSet.set ) { crgUnregisterObject( m_context, descriptorSet.set ); descriptorSet.writes.clear(); descriptorSet.set = {}; } } if ( m_descriptorSetPool ) { crgUnregisterObject( m_context, m_descriptorSetPool ); m_context.vkDestroyDescriptorPool( m_context.device , m_descriptorSetPool , m_context.allocator ); m_descriptorSetPool = {}; } for ( auto & pipeline : m_pipelines ) { if ( pipeline != VkPipeline{} ) { crgUnregisterObject( m_context, pipeline ); m_context.vkDestroyPipeline( m_context.device , pipeline , m_context.allocator ); pipeline = {}; } } if ( m_pipelineLayout ) { crgUnregisterObject( m_context, m_pipelineLayout ); m_context.vkDestroyPipelineLayout( m_context.device , m_pipelineLayout , m_context.allocator ); m_pipelineLayout = {}; } if ( m_descriptorSetLayout ) { crgUnregisterObject( m_context, m_descriptorSetLayout ); m_context.vkDestroyDescriptorSetLayout( m_context.device , m_descriptorSetLayout , m_context.allocator ); m_descriptorSetLayout = {}; } } VkPipelineShaderStageCreateInfoArray const & PipelineHolder::getProgram( uint32_t index ) { if ( m_baseConfig.m_programCreator.create ) { assert( m_baseConfig.m_programCreator.maxCount > index ); m_baseConfig.m_programs[index] = m_baseConfig.m_programCreator.create( index ); } if ( m_baseConfig.m_programs.size() == 1u ) { return m_baseConfig.m_programs[0]; } assert( m_baseConfig.m_programs.size() > index ); return m_baseConfig.m_programs[index]; } VkPipeline & PipelineHolder::getPipeline( uint32_t index ) { if ( m_baseConfig.m_programs.size() == 1u ) { assert( m_pipelines.size() == 1u ); return m_pipelines[0]; } assert( m_pipelines.size() > index ); return m_pipelines[index]; } void PipelineHolder::createPipeline( uint32_t index , std::string const & name , VkGraphicsPipelineCreateInfo const & createInfo ) { if ( m_context.vkCreateGraphicsPipelines ) { auto & pipeline = getPipeline( index ); auto res = m_context.vkCreateGraphicsPipelines( m_context.device , m_context.cache , 1u , &createInfo , m_context.allocator , &pipeline ); crg::checkVkResult( res, name + " - Pipeline creation" ); crgRegisterObject( m_context, name, pipeline ); } } void PipelineHolder::createPipeline( uint32_t index , VkGraphicsPipelineCreateInfo const & createInfo ) { createPipeline( index , m_pass.getGroupName() , createInfo ); } void PipelineHolder::createPipeline( uint32_t index , std::string const & name , VkComputePipelineCreateInfo const & createInfo ) { if ( m_context.vkCreateComputePipelines ) { auto & pipeline = getPipeline( index ); auto res = m_context.vkCreateComputePipelines( m_context.device , m_context.cache , 1u , &createInfo , m_context.allocator , &pipeline ); checkVkResult( res, name + " - Pipeline creation" ); crgRegisterObject( m_context, name, pipeline ); } } void PipelineHolder::createPipeline( uint32_t index , VkComputePipelineCreateInfo const & createInfo ) { createPipeline( index , m_pass.getGroupName() , createInfo ); } void PipelineHolder::recordInto( RecordContext const & context , VkCommandBuffer commandBuffer , uint32_t index ) { createDescriptorSet( index ); auto & pipeline = getPipeline( index ); context->vkCmdBindPipeline( commandBuffer, m_bindingPoint, pipeline ); context->vkCmdBindDescriptorSets( commandBuffer, m_bindingPoint, m_pipelineLayout, 0u, 1u, &m_descriptorSets[index].set, 0u, nullptr ); } void PipelineHolder::resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config ) { bool hadPipelineLayout{}; if ( m_pipelineLayout ) { hadPipelineLayout = true; auto pipelineLayout = m_pipelineLayout; m_context.delQueue.push( [pipelineLayout]( GraphContext & context ) { crgUnregisterObject( context, pipelineLayout ); context.vkDestroyPipelineLayout( context.device , pipelineLayout , context.allocator ); } ); m_pipelineLayout = {}; } m_baseConfig.layouts( layouts ); m_baseConfig.pushConstants( ranges ); if ( hadPipelineLayout ) doCreatePipelineLayout(); auto count = uint32_t( m_pipelines.size() ); for ( uint32_t i = 0; i < count; ++i ) resetPipeline( config, i ); } void PipelineHolder::resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ) { assert( m_pipelines.size() == 1u || index < m_pipelines.size() ); if ( m_pipelines.size() == 1u ) { index = 0u; } if ( m_pipelines[index] ) { auto pipeline = m_pipelines[index]; m_context.delQueue.push( [pipeline]( GraphContext & context ) { crgUnregisterObject( context, pipeline ); context.vkDestroyPipeline( context.device , pipeline , context.allocator ); } ); m_pipelines[index] = {}; } if ( !config.empty() ) { assert( m_baseConfig.m_programs.size() > index ); m_baseConfig.m_programs[index] = std::move( config ); } } void PipelineHolder::createDescriptorSet( uint32_t index ) { auto & descriptorSet = m_descriptorSets[index]; if ( descriptorSet.set != VkDescriptorSet{} ) { return; } pphdr::createDescriptorWrites( m_pass.getUniforms(), index, m_graph, descriptorSet.writes ); pphdr::createDescriptorWrites( m_pass.getSampled(), index, m_graph, descriptorSet.writes ); pphdr::createDescriptorWrites( m_pass.getInputs(), index, m_graph, descriptorSet.writes ); pphdr::createDescriptorWrites( m_pass.getInouts(), index, m_graph, descriptorSet.writes ); pphdr::createDescriptorWrites( m_pass.getOutputs(), index, m_graph, descriptorSet.writes ); VkDescriptorSetAllocateInfo allocateInfo{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO , nullptr , m_descriptorSetPool , 1u , &m_descriptorSetLayout }; auto res = m_context.vkAllocateDescriptorSets( m_context.device , &allocateInfo , &descriptorSet.set ); checkVkResult( res, m_pass.getGroupName() + " - DescriptorSet allocation" ); crgRegisterObject( m_context, m_pass.getGroupName(), descriptorSet.set ); for ( auto const & write : descriptorSet.writes ) { write.update( descriptorSet.set ); } auto descriptorWrites = makeVkArray< VkWriteDescriptorSet >( descriptorSet.writes ); m_context.vkUpdateDescriptorSets( m_context.device , uint32_t( descriptorWrites.size() ) , descriptorWrites.data() , 0u , nullptr ); } void PipelineHolder::doFillDescriptorBindings() { m_descriptorBindings.clear(); auto shaderStage = VkShaderStageFlags( ( VK_PIPELINE_BIND_POINT_COMPUTE == m_bindingPoint ) ? VK_SHADER_STAGE_COMPUTE_BIT : ( VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT ) ); pphdr::createDescriptorBindings( m_pass.getUniforms(), shaderStage, m_graph, m_descriptorBindings ); pphdr::createDescriptorBindings( m_pass.getSampled(), shaderStage, m_graph, m_descriptorBindings ); pphdr::createDescriptorBindings( m_pass.getInputs(), shaderStage, m_graph, m_descriptorBindings ); pphdr::createDescriptorBindings( m_pass.getInouts(), shaderStage, m_graph, m_descriptorBindings ); pphdr::createDescriptorBindings( m_pass.getOutputs(), shaderStage, m_graph, m_descriptorBindings ); } void PipelineHolder::doCreateDescriptorSetLayout() { if ( m_context.vkCreateDescriptorSetLayout ) { VkDescriptorSetLayoutCreateInfo createInfo{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO , nullptr , 0u , static_cast< uint32_t >( m_descriptorBindings.size() ) , m_descriptorBindings.data() }; auto res = m_context.vkCreateDescriptorSetLayout( m_context.device , &createInfo , m_context.allocator , &m_descriptorSetLayout ); checkVkResult( res, m_pass.getGroupName() + " - DescriptorSetLayout creation" ); crgRegisterObject( m_context, m_pass.getGroupName(), m_descriptorSetLayout ); } } void PipelineHolder::doCreatePipelineLayout() { if ( m_context.vkCreatePipelineLayout ) { std::vector< VkDescriptorSetLayout > layouts; layouts.push_back( m_descriptorSetLayout ); layouts.insert( layouts.end() , m_baseConfig.m_layouts.begin() , m_baseConfig.m_layouts.end() ); VkPipelineLayoutCreateInfo createInfo{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO , nullptr , 0u , uint32_t( layouts.size() ) , layouts.data() , uint32_t( m_baseConfig.m_pushConstants.size() ) , m_baseConfig.m_pushConstants.data() }; auto res = m_context.vkCreatePipelineLayout( m_context.device , &createInfo , m_context.allocator , &m_pipelineLayout ); checkVkResult( res, m_pass.getGroupName() + " - PipeliineLayout creation" ); crgRegisterObject( m_context, m_pass.getGroupName(), m_pipelineLayout ); } } void PipelineHolder::doCreateDescriptorPool() { if ( m_context.vkCreateDescriptorPool ) { assert( m_descriptorSetLayout ); auto sizes = getBindingsSizes( m_descriptorBindings, uint32_t( m_descriptorSets.size() ) ); VkDescriptorPoolCreateInfo createInfo{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO , nullptr , 0u , uint32_t( m_descriptorSets.size() ) , uint32_t( sizes.size() ) , sizes.data() }; auto res = m_context.vkCreateDescriptorPool( m_context.device , &createInfo , m_context.allocator , &m_descriptorSetPool ); checkVkResult( res, m_pass.getGroupName() + " - DescriptorPool creation" ); crgRegisterObject( m_context, m_pass.getGroupName(), m_descriptorSetPool ); } } } ================================================ FILE: source/RenderGraph/RunnablePasses/RenderMesh.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/RenderMesh.hpp" namespace crg { RenderMesh::RenderMesh( FramePass const & pass , GraphContext & context , RunnableGraph & graph , ru::Config const & ruConfig , rm::Config rmConfig ) : RunnablePass{ pass , context , graph , { defaultV< InitialiseCallback > , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eColorAttachmentOutput ); } ) , [this]( RecordContext & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , GetPassIndexCallback( [this](){ return m_renderMesh.getPassIndex(); } ) , IsEnabledCallback( [this](){ return m_renderMesh.isEnabled(); } ) } , { ruConfig.maxPassCount , true /*resettable*/ , ruConfig.prePassActions , ruConfig.postPassActions , ruConfig.implicitImageActions , ruConfig.implicitBufferActions } } , m_renderMesh{ pass , context , graph , std::move( rmConfig ) , ruConfig.maxPassCount } , m_renderPass{ pass , context , graph , ruConfig.maxPassCount , m_renderMesh.getRenderSize() } { } void RenderMesh::resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config , uint32_t index ) { bool hadCommandBuffer = resetCommandBuffer( index ); m_renderMesh.resetPipelineLayout( layouts, ranges, config, index ); if ( hadCommandBuffer ) reRecordCurrent(); } void RenderMesh::resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ) { resetCommandBuffer( index ); m_renderMesh.resetPipeline( std::move( config ), index ); reRecordCurrent(); } void RenderMesh::doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ) { if ( m_renderPass.initialise( context, *this, index ) ) { m_renderMesh.cleanup(); m_renderMesh.initialise( m_renderPass.getRenderSize() , m_renderPass.getRenderPass( index ) , m_renderPass.createBlendState() , index ); } m_renderPass.begin( context, commandBuffer, VK_SUBPASS_CONTENTS_INLINE, index ); m_renderMesh.record( context, commandBuffer, index ); m_renderPass.end( context, commandBuffer ); m_renderMesh.end( context, commandBuffer, index ); } } ================================================ FILE: source/RenderGraph/RunnablePasses/RenderMeshHolder.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/RenderMeshHolder.hpp" namespace crg { //********************************************************************************************* RenderMeshHolder::RenderMeshHolder( FramePass const & pass , GraphContext & context , RunnableGraph & graph , rm::Config config , uint32_t maxPassCount ) : m_graph{ graph } , m_config{ config.m_renderPosition ? std::move( *config.m_renderPosition ) : getDefaultV< Offset2D >() , config.m_depthStencilState ? std::move( *config.m_depthStencilState ) : getDefaultV< VkPipelineDepthStencilStateCreateInfo >() , config.m_getPassIndex ? std::move( *config.m_getPassIndex ) : getDefaultV< RunnablePass::GetPassIndexCallback >() , config.m_isEnabled ? std::move( *config.m_isEnabled ) : getDefaultV< RunnablePass::IsEnabledCallback >() , config.m_recordInto ? std::move( *config.m_recordInto ) : getDefaultV< RunnablePass::RecordCallback >() , config.m_end ? std::move( *config.m_end ) : getDefaultV< RunnablePass::RecordCallback >() , config.m_getPrimitiveCount ? std::move( *config.m_getPrimitiveCount ) : getDefaultV< GetPrimitiveCountCallback >() , config.m_getVertexCount ? std::move( *config.m_getVertexCount ) : getDefaultV< GetVertexCountCallback >() , config.m_getIndexType ? std::move( *config.m_getIndexType ) : getDefaultV< GetIndexTypeCallback >() , config.m_getCullMode ? std::move( *config.m_getCullMode ) : getDefaultV< GetCullModeCallback >() , config.m_vertexBuffer ? std::move( *config.m_vertexBuffer ) : getDefaultV< VertexBuffer >() , config.m_indexBuffer ? std::move( *config.m_indexBuffer ) : getDefaultV< IndexBuffer >() , config.m_indirectBuffer ? *config.m_indirectBuffer : getDefaultV< IndirectBuffer >() } , m_pipeline{ pass , context , graph , config.m_baseConfig , VK_PIPELINE_BIND_POINT_GRAPHICS , maxPassCount } , m_renderSize{ config.m_renderSize ? *config.m_renderSize : getDefaultV< Extent2D >() } { m_iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO , nullptr , 0u , VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST , VK_FALSE }; m_msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO , nullptr , 0u , VK_SAMPLE_COUNT_1_BIT , VK_FALSE , 0.0f , nullptr , VK_FALSE , VK_FALSE }; m_rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO , nullptr , 0u , VK_FALSE , VK_FALSE , VK_POLYGON_MODE_FILL , m_config.getCullMode() , VK_FRONT_FACE_COUNTER_CLOCKWISE , VK_FALSE , 0.0f , 0.0f , 0.0f , 0.0f }; } void RenderMeshHolder::initialise( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState , uint32_t index ) { m_pipeline.initialise(); if ( !m_renderPass ) { doPreparePipelineStates( renderSize, renderPass, std::move( blendState ) ); } else if ( m_renderPass != renderPass ) { resetRenderPass( renderSize, renderPass, std::move( blendState ), index ); } doCreatePipeline( index ); } void RenderMeshHolder::cleanup() { m_pipeline.cleanup(); } void RenderMeshHolder::resetRenderPass( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState , uint32_t index ) { m_pipeline.resetPipeline( {}, index ); doPreparePipelineStates( renderSize, renderPass, std::move( blendState ) ); doCreatePipeline( index ); } void RenderMeshHolder::resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config , uint32_t index ) { m_pipeline.resetPipelineLayout( layouts, ranges, config ); if ( m_renderPass ) { doCreatePipeline( index ); } } void RenderMeshHolder::resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ) { m_pipeline.resetPipeline( std::move( config ), index ); if ( m_renderPass ) { doCreatePipeline( index ); } } void RenderMeshHolder::record( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ) { doCreatePipeline( index ); m_pipeline.recordInto( context, commandBuffer, index ); m_config.recordInto( context, commandBuffer, index ); if ( m_config.vertexBuffer.buffer != BufferViewId{} ) { auto vkBuffer = m_graph.createBuffer( m_config.vertexBuffer.buffer.data->buffer ); context->vkCmdBindVertexBuffers( commandBuffer, 0u, 1u, &vkBuffer, &getSubresourceRange( m_config.vertexBuffer.buffer ).offset ); } if ( m_config.indirectBuffer != defaultV< IndirectBuffer > ) { auto indirectBuffer = m_graph.createBuffer( m_config.indirectBuffer.buffer.data->buffer ); if ( m_config.indexBuffer != defaultV< IndexBuffer > ) { auto indexBuffer = m_graph.createBuffer( m_config.indexBuffer.buffer.data->buffer ); context->vkCmdBindIndexBuffer( commandBuffer, indexBuffer, getSubresourceRange( m_config.indexBuffer.buffer ).offset, m_config.getIndexType() ); context->vkCmdDrawIndexedIndirect( commandBuffer, indirectBuffer, getSubresourceRange( m_config.indirectBuffer.buffer ).offset, 1u, m_config.indirectBuffer.stride ); } else { context->vkCmdDrawIndirect( commandBuffer, indirectBuffer, getSubresourceRange( m_config.indirectBuffer.buffer ).offset, 1u, m_config.indirectBuffer.stride ); } } else if ( m_config.indexBuffer != defaultV< IndexBuffer > ) { auto indexBuffer = m_graph.createBuffer( m_config.indexBuffer.buffer.data->buffer ); context->vkCmdBindIndexBuffer( commandBuffer, indexBuffer, getSubresourceRange( m_config.indexBuffer.buffer ).offset, m_config.getIndexType() ); context->vkCmdDrawIndexed( commandBuffer, m_config.getPrimitiveCount(), 1u, 0u, 0u, 0u ); } else { context->vkCmdDraw( commandBuffer, m_config.getVertexCount(), 1u, 0u, 0u ); } } void RenderMeshHolder::end( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const { m_config.end( context, commandBuffer, index ); } uint32_t RenderMeshHolder::getPassIndex()const { return m_config.getPassIndex(); } bool RenderMeshHolder::isEnabled()const { return m_config.isEnabled(); } Extent2D RenderMeshHolder::getRenderSize()const { return m_renderSize; } void RenderMeshHolder::doPreparePipelineStates( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState ) { m_vpState = doCreateViewportState( renderSize, m_viewport, m_scissor ); m_renderSize = renderSize; m_renderPass = renderPass; m_blendAttachs = { blendState.pAttachments, blendState.pAttachments + blendState.attachmentCount }; m_blendState = std::move( blendState ); m_blendState.pAttachments = m_blendAttachs.data(); } void RenderMeshHolder::doCreatePipeline( uint32_t index ) { if ( m_pipeline.getPipeline( index ) ) { return; } auto & program = m_pipeline.getProgram( index ); VkGraphicsPipelineCreateInfo createInfo{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO , nullptr , 0u , uint32_t( program.size() ) , program.data() , &m_config.vertexBuffer.inputState , &m_iaState , nullptr , &m_vpState , &m_rsState , &m_msState , &m_config.depthStencilState , &m_blendState , nullptr , m_pipeline.getPipelineLayout() , m_renderPass , 0u , VkPipeline{} , 0u }; m_pipeline.createPipeline( index, createInfo ); } VkPipelineViewportStateCreateInfo RenderMeshHolder::doCreateViewportState( Extent2D const & renderSize , VkViewport & viewport , VkRect2D & scissor )const { viewport = { float( m_config.renderPosition.x ) , float( m_config.renderPosition.y ) , float( renderSize.width ) , float( renderSize.height ) , 0.0f , 1.0f }; scissor = { { m_config.renderPosition.x, m_config.renderPosition.y } , { renderSize.width, renderSize.height } }; return { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO , nullptr , 0u , 1u , & viewport , 1u , & scissor }; } } ================================================ FILE: source/RenderGraph/RunnablePasses/RenderPass.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/RenderPass.hpp" #include "RenderGraph/Attachment.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/RunnableGraph.hpp" #include namespace crg { //********************************************************************************************* VkDescriptorPoolSizeArray getBindingsSizes( VkDescriptorSetLayoutBindingArray const & bindings , uint32_t maxSets ) { VkDescriptorPoolSizeArray result; for ( auto & binding : bindings ) { auto it = std::find_if( result.begin(), result.end() , [&binding]( VkDescriptorPoolSize const & lookup ) { return binding.descriptorType == lookup.type; } ); if ( it == result.end() ) result.push_back( { binding.descriptorType, binding.descriptorCount * maxSets } ); else it->descriptorCount += binding.descriptorCount * maxSets; } return result; } //********************************************************************************************* RenderPass::Callbacks::Callbacks( InitialiseCallback initialise , RecordCallback record ) : Callbacks{ std::move( initialise ) , std::move( record ) , getDefaultV< GetSubpassContentsCallback >() , getDefaultV< GetPassIndexCallback >() , getDefaultV< IsEnabledCallback >() } { } RenderPass::Callbacks::Callbacks( InitialiseCallback initialise , RecordCallback record , GetSubpassContentsCallback getSubpassContents ) : Callbacks{ std::move( initialise ) , std::move( record ) , std::move( getSubpassContents ) , getDefaultV< GetPassIndexCallback >() , getDefaultV< IsEnabledCallback >() } { } RenderPass::Callbacks::Callbacks( InitialiseCallback initialise , RecordCallback record , GetSubpassContentsCallback getSubpassContents , GetPassIndexCallback getPassIndex ) : Callbacks{ std::move( initialise ) , std::move( record ) , std::move( getSubpassContents ) , std::move( getPassIndex ) , getDefaultV< IsEnabledCallback >() } { } RenderPass::Callbacks::Callbacks( InitialiseCallback initialise , RecordCallback record , GetSubpassContentsCallback getSubpassContents , GetPassIndexCallback getPassIndex , IsEnabledCallback isEnabled ) : initialise{ std::move( initialise ) } , record{ std::move( record ) } , getSubpassContents{ std::move( getSubpassContents ) } , getPassIndex{ std::move( getPassIndex ) } , isEnabled{ std::move( isEnabled ) } { } //********************************************************************************************* RenderPass::RenderPass( FramePass const & pass , GraphContext & context , RunnableGraph & graph , Callbacks callbacks , Extent2D size , ru::Config const & ruConfig ) : RunnablePass{ pass , context , graph , { defaultV< InitialiseCallback > , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eColorAttachmentOutput ); } ) , [this]( RecordContext & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , std::move( callbacks.getPassIndex ) , std::move( callbacks.isEnabled ) } , ruConfig } , m_rpCallbacks{ std::move( callbacks ) } , m_holder{ pass , context , graph , ruConfig.maxPassCount , std::move( size ) } { } void RenderPass::doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ) { if ( m_holder.initialise( context, *this, index ) ) { m_rpCallbacks.initialise( index ); } m_holder.begin( context , commandBuffer , m_rpCallbacks.getSubpassContents() , index ); m_rpCallbacks.record( context , commandBuffer , index ); m_holder.end( context , commandBuffer ); } //********************************************************************************************* } ================================================ FILE: source/RenderGraph/RunnablePasses/RenderPassHolder.cpp ================================================ /* See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/RenderPassHolder.hpp" #include "RenderGraph/Attachment.hpp" #include "RenderGraph/GraphContext.hpp" #include "RenderGraph/Log.hpp" #include "RenderGraph/RunnableGraph.hpp" #include namespace crg { //********************************************************************************************* namespace rpHolder { static VkAttachmentReference addAttach( RecordContext & context , Attachment const & attach , ImageViewId view , VkAttachmentDescriptionArray & attaches , std::vector< RenderPassHolder::Entry > & viewAttaches , std::vector< VkClearValue > & clearValues , LayoutState initialLayout , LayoutState finalLayout , bool separateDepthStencilLayouts ) { VkAttachmentReference result{ uint32_t( attaches.size() ) , convert( attach.getImageLayout( separateDepthStencilLayouts ) ) }; attaches.push_back( { 0u , convert( view.data->info.format ) , convert( view.data->image.data->info.samples ) , convert( initialLayout.layout == ImageLayout::eUndefined ? AttachmentLoadOp::eClear : attach.getLoadOp() ) , convert( attach.getStoreOp() ) , convert( attach.getStencilLoadOp() ) , convert( attach.getStencilStoreOp() ) , convert( initialLayout.layout ) , convert( finalLayout.layout ) } ); viewAttaches.emplace_back( view, initialLayout, finalLayout ); clearValues.push_back( convert( attach.getClearValue() ) ); if ( view.data->source.empty() ) { context.setLayoutState( view, finalLayout ); } else { for ( auto & source : view.data->source ) { context.setLayoutState( source, finalLayout ); } } return result; } static VkAttachmentReference addAttach( RecordContext & context , Attachment const & attach , ImageViewId view , VkAttachmentDescriptionArray & attaches , std::vector< RenderPassHolder::Entry > & viewAttaches , std::vector< VkClearValue > & clearValues , VkPipelineColorBlendAttachmentStateArray & blendAttachs , LayoutState initialLayout , LayoutState finalLayout , bool separateDepthStencilLayouts ) { blendAttachs.push_back( convert( attach.getBlendState() ) ); return addAttach( context , attach , view , attaches , viewAttaches , clearValues , initialLayout , finalLayout , separateDepthStencilLayouts ); } static bool operator!=( LayoutState const & lhs, LayoutState const & rhs ) { return lhs.layout != rhs.layout || lhs.state.access != rhs.state.access || ( lhs.state.access != AccessFlags::eNone && lhs.state.pipelineStage != rhs.state.pipelineStage ); } static bool checkAttaches( RecordContext const & context , std::vector< RenderPassHolder::Entry > const & attaches , uint32_t passIndex ) { auto it = std::find_if( attaches.begin(), attaches.end() , [&context, passIndex]( RenderPassHolder::Entry const & lookup ) { auto layout = context.getLayoutState( resolveView( lookup.view, passIndex ) ); return layout != lookup.input && layout.layout != ImageLayout::eUndefined; } ); return it == attaches.end(); } } //********************************************************************************************* void RenderPassHolder::PassData::cleanup( crg::GraphContext & context )noexcept { attaches.clear(); clearValues.clear(); if ( frameBuffer ) { crgUnregisterObject( context, frameBuffer ); context.vkDestroyFramebuffer( context.device , frameBuffer , context.allocator ); frameBuffer = {}; } if ( renderPass ) { crgUnregisterObject( context, renderPass ); context.vkDestroyRenderPass( context.device , renderPass , context.allocator ); renderPass = {}; } } //********************************************************************************************* RenderPassHolder::RenderPassHolder( FramePass const & pass , GraphContext & context , RunnableGraph & graph , uint32_t maxPassCount , Extent2D size ) : m_pass{ pass } , m_context{ context } , m_graph{ graph } , m_size{ std::move( size ) } { m_passes.resize( maxPassCount ); } RenderPassHolder::~RenderPassHolder()noexcept { for ( auto & data : m_passes ) { data.cleanup( m_context ); } } bool RenderPassHolder::initialise( RecordContext & context , crg::RunnablePass const & runnable , uint32_t passIndex ) { auto & data = m_passes[passIndex]; auto previousState = context.getPrevPipelineState(); auto nextState = context.getNextPipelineState(); if ( data.renderPass && rpHolder::checkAttaches( context, data.attaches, passIndex ) && data.previousState == previousState && data.nextState == nextState ) { return false; } data.cleanup( m_context ); doCreateRenderPass( context , runnable , previousState , nextState , passIndex ); doInitialiseRenderArea( passIndex ); return true; } VkRenderPassBeginInfo RenderPassHolder::getBeginInfo( uint32_t index )const { auto frameBuffer = getFramebuffer( index ); return VkRenderPassBeginInfo{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO , nullptr , getRenderPass( index ) , frameBuffer , convert( getRenderArea( index ) ) , uint32_t( getClearValues( index ).size() ) , getClearValues( index ).data() }; } void RenderPassHolder::begin( RecordContext & context , VkCommandBuffer commandBuffer , VkSubpassContents subpassContents , uint32_t index ) { m_index = index; m_currentPass = &m_passes[m_index]; for ( auto & attach : m_currentPass->attaches ) { context.setLayoutState( resolveView( attach.view, m_index ) , attach.input ); } auto beginInfo = getBeginInfo( m_index ); m_context.vkCmdBeginRenderPass( commandBuffer , &beginInfo , subpassContents ); } void RenderPassHolder::end( RecordContext & context , VkCommandBuffer commandBuffer ) { m_context.vkCmdEndRenderPass( commandBuffer ); for ( auto & attach : m_currentPass->attaches ) { context.setLayoutState( resolveView( attach.view, m_index ) , attach.output ); } m_currentPass = nullptr; } void RenderPassHolder::doCreateRenderPass( RecordContext & context , crg::RunnablePass const & runnable , PipelineState const & previousState , PipelineState const & nextState , uint32_t passIndex ) { VkAttachmentDescriptionArray attaches; VkAttachmentReferenceArray colorReferences; VkAttachmentReference depthReference{}; auto & data = m_passes[passIndex]; m_blendAttachs.clear(); for ( auto attach : m_pass.getTargets() ) { auto view = attach->view( passIndex ); auto resolved = resolveView( view, passIndex ); auto currentLayout = m_graph.getCurrentLayoutState( context, resolved ); auto nextLayout = m_graph.getNextLayoutState( context, runnable, resolved ); auto from = ( !attach->isInput() ? crg::makeLayoutState( ImageLayout::eUndefined ) : currentLayout ); checkUndefinedInput( "RenderPass", *attach, resolved, from.layout ); if ( attach->isDepthImageTarget() || attach->isStencilImageTarget() ) { depthReference = rpHolder::addAttach( context , *attach , view , attaches , data.attaches , data.clearValues , from , nextLayout , m_context.separateDepthStencilLayouts ); } else if ( attach->isColourImageTarget() ) { colorReferences.push_back( rpHolder::addAttach( context , *attach , view , attaches , data.attaches , data.clearValues , m_blendAttachs , from , nextLayout , m_context.separateDepthStencilLayouts ) ); } } VkSubpassDescription subpassDesc{ 0u , VK_PIPELINE_BIND_POINT_GRAPHICS , 0u , nullptr , uint32_t( colorReferences.size() ) , colorReferences.data() , nullptr , depthReference.layout ? &depthReference : nullptr , 0u , nullptr }; data.previousState = previousState; data.nextState = nextState; VkSubpassDependencyArray dependencies{ { VK_SUBPASS_EXTERNAL , 0u , getPipelineStageFlags( previousState.pipelineStage ) , VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , getAccessFlags( previousState.access ) , VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT , VK_DEPENDENCY_BY_REGION_BIT } , { 0u , VK_SUBPASS_EXTERNAL , VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , getPipelineStageFlags( nextState.pipelineStage ) , VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT , getAccessFlags( nextState.access ) , VK_DEPENDENCY_BY_REGION_BIT } }; VkRenderPassCreateInfo createInfo{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO , nullptr , 0u , uint32_t( attaches.size() ) , attaches.data() , 1u , &subpassDesc , uint32_t( dependencies.size() ) , dependencies.data() }; auto res = m_context.vkCreateRenderPass( m_context.device , &createInfo , m_context.allocator , &data.renderPass ); checkVkResult( res, m_pass.getGroupName() + " - RenderPass creation" ); crgRegisterObject( m_context, m_pass.getGroupName() + std::to_string( m_count++ ), data.renderPass ); } VkPipelineColorBlendStateCreateInfo RenderPassHolder::createBlendState() { return { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO , nullptr , 0u , VK_FALSE , VK_LOGIC_OP_COPY , uint32_t( m_blendAttachs.size() ) , m_blendAttachs.data() , {} }; } VkFramebuffer RenderPassHolder::getFramebuffer( uint32_t index )const { return doCreateFramebuffer( index ); } void RenderPassHolder::doInitialiseRenderArea( uint32_t index ) { uint32_t width{ m_size.width }; uint32_t height{ m_size.height }; m_passes[index].attachments.clear(); m_layers = 1u; for ( auto attach : m_pass.getTargets() ) { auto view = attach->view(); m_passes[index].attachments.push_back( attach ); width = std::max( width , view.data->image.data->info.extent.width >> getSubresourceRange( view ).baseMipLevel ); height = std::max( height , view.data->image.data->info.extent.height >> getSubresourceRange( view ).baseMipLevel ); m_layers = std::max( m_layers , getSubresourceRange( view ).layerCount ); } m_passes[index].renderArea.extent.width = width; m_passes[index].renderArea.extent.height = height; } VkFramebuffer RenderPassHolder::doCreateFramebuffer( uint32_t passIndex )const { auto & data = m_passes[passIndex]; auto frameBuffer = &data.frameBuffer; if ( !*frameBuffer ) { VkImageViewArray attachments; for ( auto & attach : data.attachments ) { attachments.push_back( m_graph.createImageView( attach->view( passIndex ) ) ); } VkFramebufferCreateInfo createInfo{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO , nullptr , 0u , data.renderPass , uint32_t( attachments.size() ) , attachments.data() , data.renderArea.extent.width , data.renderArea.extent.height , m_layers }; auto res = m_context.vkCreateFramebuffer( m_context.device , &createInfo , m_context.allocator , frameBuffer ); auto name = m_pass.getGroupName() + std::string( "[" ) + std::to_string( passIndex ) + std::string( "]" ); checkVkResult( res, name + " - Framebuffer creation" ); crgRegisterObject( m_context, name, *frameBuffer ); } return *frameBuffer; } } ================================================ FILE: source/RenderGraph/RunnablePasses/RenderQuad.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/RenderQuad.hpp" namespace crg { RenderQuad::RenderQuad( FramePass const & pass , GraphContext & context , RunnableGraph & graph , ru::Config const & ruConfig , rq::Config rqConfig ) : RunnablePass{ pass , context , graph , { defaultV< InitialiseCallback > , GetPipelineStateCallback( [](){ return crg::getPipelineState( PipelineStageFlags::eColorAttachmentOutput ); } ) , [this]( RecordContext & recContext, VkCommandBuffer cb, uint32_t i ){ doRecordInto( recContext, cb, i ); } , GetPassIndexCallback( [this](){ return m_renderQuad.getPassIndex(); } ) , IsEnabledCallback( [this](){ return m_renderQuad.isEnabled(); } ) } , { ruConfig.maxPassCount , true /*resettable*/ , ruConfig.prePassActions , ruConfig.postPassActions , ruConfig.implicitImageActions , ruConfig.implicitBufferActions } } , m_renderQuad{ pass , context , graph , rqConfig , ruConfig.maxPassCount } , m_renderPass{ pass , context , graph , ruConfig.maxPassCount , rqConfig.m_renderSize ? *rqConfig.m_renderSize : getDefaultV< Extent2D >() } { } void RenderQuad::resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config , uint32_t index ) { bool hadCommandBuffer = resetCommandBuffer( index ); m_renderQuad.resetPipelineLayout( layouts, ranges, config, index ); if ( hadCommandBuffer ) reRecordCurrent(); } void RenderQuad::resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ) { resetCommandBuffer( index ); m_renderQuad.resetPipeline( std::move( config ), index ); reRecordCurrent(); } void RenderQuad::doRecordInto( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ) { if ( m_renderPass.initialise( context, *this, index ) ) { m_renderQuad.initialise( m_renderPass.getRenderSize() , m_renderPass.getRenderPass( index ) , m_renderPass.createBlendState() , index ); } m_renderPass.begin( context, commandBuffer, VK_SUBPASS_CONTENTS_INLINE, index ); m_renderQuad.record( context, commandBuffer, index ); m_renderPass.end( context, commandBuffer ); m_renderQuad.end( context, commandBuffer, index ); } } ================================================ FILE: source/RenderGraph/RunnablePasses/RenderQuadHolder.cpp ================================================ /* This file belongs to FrameGraph. See LICENSE file in root folder. */ #include "RenderGraph/RunnablePasses/RenderQuadHolder.hpp" #include "RenderGraph/GraphContext.hpp" namespace crg { namespace rdqdhdr { static bool isPtrEnabled( bool const * v ) { return v ? *v : true; } } RenderQuadHolder::RenderQuadHolder( FramePass const & pass , GraphContext & context , RunnableGraph & graph , rq::Config config , uint32_t maxPassCount ) : m_config{ config.m_texcoordConfig ? std::move( *config.m_texcoordConfig ) : getDefaultV< Texcoord >() , config.m_renderPosition ? std::move( *config.m_renderPosition ) : getDefaultV< Offset2D >() , config.m_depthStencilState ? std::move( *config.m_depthStencilState ) : getDefaultV< VkPipelineDepthStencilStateCreateInfo >() , config.m_passIndex.has_value() ? *config.m_passIndex : getDefaultV< uint32_t const * >() , config.m_enabled.has_value() ? *config.m_enabled : getDefaultV< bool const * >() , config.m_isEnabled , config.m_recordInto ? std::move( *config.m_recordInto ) : getDefaultV< RunnablePass::RecordCallback >() , config.m_end ? std::move( *config.m_end ) : getDefaultV< RunnablePass::RecordCallback >() , config.m_instances.has_value() ? *config.m_instances : 1u , config.m_indirectBuffer ? *config.m_indirectBuffer : getDefaultV < IndirectBuffer >() } , m_graph{ graph } , m_pipeline{ pass , context , graph , config.m_baseConfig , VK_PIPELINE_BIND_POINT_GRAPHICS , maxPassCount } , m_useTexCoord{ config.m_texcoordConfig } { m_iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO , nullptr , 0u , VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP , VK_FALSE }; m_msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO , nullptr , 0u , VK_SAMPLE_COUNT_1_BIT , VK_FALSE , 0.0f , nullptr , VK_FALSE , VK_FALSE }; m_rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO , nullptr , 0u , VK_FALSE , VK_FALSE , VK_POLYGON_MODE_FILL , VK_CULL_MODE_NONE , VK_FRONT_FACE_COUNTER_CLOCKWISE , VK_FALSE , 0.0f , 0.0f , 0.0f , 0.0f }; } void RenderQuadHolder::initialise( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState , uint32_t index ) { if ( !m_vertexBuffer ) { m_pipeline.initialise(); m_vertexBuffer = &m_graph.createQuadTriVertexBuffer( m_useTexCoord , m_config.texcoordConfig ); doPreparePipelineStates( renderSize, renderPass, std::move( blendState ) ); } else if ( m_renderPass != renderPass ) { resetRenderPass( renderSize, renderPass, std::move( blendState ), index ); } doCreatePipeline( index ); } void RenderQuadHolder::resetRenderPass( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState , uint32_t index ) { m_pipeline.resetPipeline( {}, index ); doPreparePipelineStates( renderSize, renderPass, std::move( blendState ) ); doCreatePipeline( index ); } void RenderQuadHolder::resetPipelineLayout( std::vector< VkDescriptorSetLayout > const & layouts , std::vector< VkPushConstantRange > const & ranges , VkPipelineShaderStageCreateInfoArray const & config , uint32_t index ) { m_pipeline.resetPipelineLayout( layouts, ranges, config ); if ( m_renderPass ) { doCreatePipeline( index ); } } void RenderQuadHolder::resetPipeline( VkPipelineShaderStageCreateInfoArray config , uint32_t index ) { m_pipeline.resetPipeline( std::move( config ), index ); if ( m_renderPass ) { doCreatePipeline( index ); } } void RenderQuadHolder::record( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ) { doCreatePipeline( index ); m_pipeline.recordInto( context, commandBuffer, index ); m_config.recordInto( context, commandBuffer, index ); if ( m_vertexBuffer ) { auto vkBuffer = m_graph.createBuffer( m_vertexBuffer->buffer.data->buffer ); context->vkCmdBindVertexBuffers( commandBuffer, 0u, 1u , &vkBuffer, &getSubresourceRange( m_vertexBuffer->buffer ).offset ); } if ( m_config.indirectBuffer != defaultV< IndirectBuffer > ) { auto indirectBuffer = m_graph.createBuffer( m_config.indirectBuffer.buffer.data->buffer ); context->vkCmdDrawIndirect( commandBuffer, indirectBuffer, getSubresourceRange( m_config.indirectBuffer.buffer ).offset, 1u, m_config.indirectBuffer.stride ); } else { context->vkCmdDraw( commandBuffer, 3u, m_config.m_instances, 0u, 0u ); } } void RenderQuadHolder::end( RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const { m_config.end( context, commandBuffer, index ); } uint32_t RenderQuadHolder::getPassIndex()const { return ( m_config.passIndex ? *m_config.passIndex : 0u ); } bool RenderQuadHolder::isEnabled()const { return ( m_config.isEnabled ? ( *m_config.isEnabled )() : rdqdhdr::isPtrEnabled( m_config.enabled ) ); } void RenderQuadHolder::doPreparePipelineStates( Extent2D const & renderSize , VkRenderPass renderPass , VkPipelineColorBlendStateCreateInfo blendState ) { m_vpState = doCreateViewportState( renderSize, m_viewport, m_scissor ); m_renderSize = renderSize; m_renderPass = renderPass; m_blendAttachs = { blendState.pAttachments, blendState.pAttachments + blendState.attachmentCount }; m_blendState = std::move( blendState ); m_blendState.pAttachments = m_blendAttachs.data(); } void RenderQuadHolder::doCreatePipeline( uint32_t index ) { if ( m_pipeline.getPipeline( index ) ) { return; } auto & program = m_pipeline.getProgram( index ); VkGraphicsPipelineCreateInfo createInfo{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, nullptr, 0u , uint32_t( program.size() ), program.data() , &getInputState(), &m_iaState, nullptr , &m_vpState, &m_rsState, &m_msState , &m_config.depthStencilState, &m_blendState, nullptr , m_pipeline.getPipelineLayout(), m_renderPass , 0u, VkPipeline{}, 0u }; m_pipeline.createPipeline( index, createInfo ); } VkPipelineViewportStateCreateInfo RenderQuadHolder::doCreateViewportState( Extent2D const & renderSize , VkViewport & viewport , VkRect2D & scissor )const { viewport = { float( m_config.renderPosition.x ) , float( m_config.renderPosition.y ) , float( renderSize.width ) , float( renderSize.height ) , 0.0f , 1.0f }; scissor = { { m_config.renderPosition.x, m_config.renderPosition.y } , { renderSize.width, renderSize.height } }; return { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, nullptr, 0u , 1u, &viewport , 1u, &scissor }; } } ================================================ FILE: test/BaseTest.cpp ================================================ #include "BaseTest.hpp" #include #if defined( _WIN32 ) # include #elif defined( __APPLE__ ) # include # include # include #elif defined( __linux__ ) # include # include # include #endif #include #include #include #include #include #include #include namespace test { using StringArray = std::vector< std::string >; //********************************************************************************************* namespace { template< typename LogStreambufTraits > class LogStreambuf : public std::streambuf { public: using string_type = std::string; using ostream_type = std::ostream; using streambuf_type = std::streambuf; using int_type = std::streambuf::int_type; using traits_type = std::streambuf::traits_type; LogStreambuf( LogStreambuf const & ) = delete; LogStreambuf & operator=( LogStreambuf const & ) = delete; LogStreambuf( LogStreambuf && ) = delete; LogStreambuf & operator=( LogStreambuf && ) = delete; explicit LogStreambuf( std::string const & name , std::ostream & stream ) : m_stream{ stream } , m_fstream{ getExecutableDirectory() + name + ".log" } { m_old = m_stream.rdbuf( this ); } ~LogStreambuf()noexcept override { try { m_stream.rdbuf( m_old ); } catch ( ... ) { // What to do here ? } } int_type overflow( int_type c = traits_type::eof() )override { if ( traits_type::eq_int_type( c, traits_type::eof() ) ) { do_sync(); } else if ( c == '\n' ) { do_sync(); } else if ( c == '\r' ) { m_buffer += '\r'; do_sync_no_nl(); } else { m_buffer += traits_type::to_char_type( c ); } return c; } int do_sync() { LogStreambufTraits::log( m_fstream, m_buffer ); m_buffer.clear(); return 0; } int do_sync_no_nl() { LogStreambufTraits::logNoNL( m_fstream, m_buffer ); m_buffer.clear(); return 0; } private: string_type m_buffer; ostream_type & m_stream; streambuf_type * m_old; std::ofstream m_fstream; }; struct DebugLogStreambufTraits { static void log( std::ostream & stream , std::string const & text ) { stream << "DEBUG: " << text << std::endl; fmt::print( "{}\n", text ); } static void logNoNL( std::ostream & stream , std::string const & text ) { stream << "DEBUG: " << text; fmt::print( "{}\n", text ); } }; struct InfoLogStreambufTraits { static void log( std::ostream & stream , std::string const & text ) { stream << text << std::endl; fmt::print( "{}\n", text ); } static void logNoNL( std::ostream & stream , std::string const & text ) { stream << text; fmt::print( "{}\n", text ); } }; struct ErrorLogStreambufTraits { static void log( std::ostream & stream , std::string const & text ) { stream << "ERROR: " << text << std::endl; fmt::print( "{}\n", text ); } static void logNoNL( std::ostream & stream , std::string const & text ) { stream << "ERROR: " << text; fmt::print( "{}\n", text ); } }; #if defined( _WIN32 ) char constexpr PathSeparator = '\\'; #else char constexpr PathSeparator = '/'; #endif std::string getPath( std::string_view path ) { return std::string{ path.substr( 0, path.find_last_of( PathSeparator ) ) }; } std::string findMissing( StringArray const & lhsLines , StringArray const & rhsLines , std::string_view const & op ) { std::string result; for ( auto & lhsLine : lhsLines ) { if ( rhsLines.end() == std::find( rhsLines.begin() , rhsLines.end() , lhsLine ) ) { result += std::string{ op } + lhsLine + "\n"; } } return result; } } //********************************************************************************************* #if defined( _WIN32 ) std::string getExecutableDirectory() { std::string result; std::array< char, FILENAME_MAX > path{}; if ( DWORD res = ::GetModuleFileNameA( nullptr , path.data() , sizeof( path ) ) ) { result = path.data(); } result = getPath( result ) + PathSeparator; return result; } #elif defined( __APPLE__ ) std::string getExecutableDirectory() { std::string result; std::array< char, FILENAME_MAX > path{}; uint32_t size = FILENAME_MAX; if ( _NSGetExecutablePath( path.data(), &size ) == 0 ) { char realPath[FILENAME_MAX]{}; realpath( path.data(), realPath ); result = std::string{ realPath }; } result = getPath( result ) + PathSeparator; return result; } #else std::string getExecutableDirectory() { std::string result; std::array< char, FILENAME_MAX > path{}; char buffer[32]; sprintf( buffer, "/proc/%d/exe", getpid() ); int bytes = std::min< std::size_t >( readlink( buffer , path.data() , path.size() ) , path.size() - 1 ); if ( bytes > 0 ) { path[bytes] = '\0'; result = path.data(); } result = getPath( result ) + PathSeparator; return result; } #endif //********************************************************************************************* StringArray splitInLines( std::string const & value ) { StringArray lines; std::stringstream iss( value ); while ( iss.good() ) { std::string line; std::getline( iss, line ); lines.push_back( line ); } return lines; } std::string TestCounts::diffLines( std::string const & check , std::string const & ref ) { auto checkLines = splitInLines( check ); auto refLines = splitInLines( ref ); std::string result; result += findMissing( checkLines, refLines, "+" ); result += findMissing( refLines, checkLines, "-" ); return result; } //********************************************************************************************* TestSuite::TestSuite( std::string const & name ) : tclog{ std::make_unique< test::LogStreambuf< test::DebugLogStreambufTraits > >( name, std::clog ) } , tcout{ std::make_unique< test::LogStreambuf< test::InfoLogStreambufTraits > >( name, std::cout ) } , tcerr{ std::make_unique< test::LogStreambuf< test::ErrorLogStreambufTraits > >( name, std::cerr ) } { crg::Logger::setTraceCallback( []( std::string_view, bool )noexcept { // Don't log trace } ); crg::Logger::setDebugCallback( []( std::string_view msg, bool newLine )noexcept { std::clog << msg.data(); if ( newLine ) std::clog << "\n"; } ); crg::Logger::setInfoCallback( []( std::string_view msg, bool newLine )noexcept { std::cout << msg.data(); if ( newLine ) std::cout << "\n"; } ); crg::Logger::setWarningCallback( []( std::string_view msg, bool newLine )noexcept { std::cout << msg.data(); if ( newLine ) std::cout << "\n"; } ); crg::Logger::setErrorCallback( []( std::string_view msg, bool newLine )noexcept { std::cerr << msg.data(); if ( newLine ) std::cerr << "\n"; } ); } //********************************************************************************************* int testsMain( int argc, char ** argv, std::string_view testSuiteName ) { std::locale::global( std::locale{ "C" } ); testing::InitGoogleTest( &argc, argv ); auto suite = std::make_unique< test::TestSuite >( std::string{ testSuiteName } ); testing::AddGlobalTestEnvironment( suite.release() ); return RUN_ALL_TESTS(); } std::string sortLines( std::string const & value ) { std::stringstream stream{ value }; std::multiset< std::string, std::less<> > sorted; std::string line; while ( std::getline( stream, line ) ) { sorted.insert( line ); } std::stringstream result; for ( auto & v : sorted ) { result << v << std::endl; } return result.str(); } //********************************************************************************************* } ================================================ FILE: test/BaseTest.hpp ================================================ #pragma once #include #include #include #include #include #include namespace test { std::string getExecutableDirectory(); struct TestCounts { explicit TestCounts( std::string const & testName ) : testName{ testName } { } static std::string diffLines( std::string const & check , std::string const & ref ); std::string testName; }; class Exception : public std::runtime_error { public: using std::runtime_error::runtime_error; }; class TestSuite : public ::testing::Environment { public: explicit TestSuite( std::string const & name ); private: std::unique_ptr< std::streambuf > tclog; std::unique_ptr< std::streambuf > tcout; std::unique_ptr< std::streambuf > tcerr; }; int testsMain( int argc, char ** argv, std::string_view testSuiteName ); std::string sortLines( std::string const & value ); #define testStringify( x )\ #x #define nameConcat( X, Y ) nameConcat_( X, Y ) #define nameConcat_( X, Y ) X ## Y #define testBegin( name )\ test::TestCounts testCounts{ name }; #define testEnd() #define require( x )\ try\ {\ if ( !( x ) )\ {\ throw test::Exception{ std::string{ testStringify( x ) } };\ }\ }\ catch ( test::Exception & exc )\ {\ GTEST_FATAL_FAILURE_( ( std::string{ testStringify( x )" failed." } + exc.what() ).c_str() );\ }\ catch ( ... )\ {\ GTEST_FATAL_FAILURE_( "Unknown unhandled exception." );\ } #define check( x )\ EXPECT_TRUE( x ); #define checkEqual( x, y )\ EXPECT_EQ( x, y ); #define checkEqualSortedLines( x, y )\ try\ {\ auto diff = test::TestCounts::diffLines( test::sortLines( x ), test::sortLines( y ) );\ if ( !diff.empty() )\ {\ throw test::Exception{ "LHS(\n" + std::string{ x } + " )\nRHS(\n" + std::string{ y } + " )\nDIFF(\n" + diff + " )" };\ }\ }\ catch ( test::Exception & exc )\ {\ GTEST_FATAL_FAILURE_( ( std::string{ #x" failed." } + exc.what() ).c_str() );\ }\ catch ( crg::Exception & exc )\ {\ GTEST_FATAL_FAILURE_( ( std::string{ #x" failed." } + exc.what() ).c_str() );\ }\ catch ( ... )\ {\ GTEST_FATAL_FAILURE_( "Unknown unhandled exception." );\ } #define checkThrow( x, excType )\ EXPECT_THROW( x, excType ); #define checkNoThrow( x )\ EXPECT_NO_THROW( x ); #define testSuiteMain()\ int main( int argc, char ** argv )\ {\ return test::testsMain( argc, argv, CRG_TestSuiteNameString );\ } } ================================================ FILE: test/CMakeLists.txt ================================================ enable_testing() set( GTest_DIR ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/share/gtest ) find_package( GTest CONFIG REQUIRED GTest::gtest GTest::gtest_main ) find_package( fmt CONFIG REQUIRED ) set( TEST_NAME TestCommon ) set( ${TEST_NAME}_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/BaseTest.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Common.hpp ) set( ${TEST_NAME}_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/BaseTest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Common.cpp ) add_library( ${TEST_NAME} STATIC ${${TEST_NAME}_HEADER_FILES} ${${TEST_NAME}_SOURCE_FILES} ) add_library( crg::${TEST_NAME} ALIAS ${TEST_NAME} ) target_sources( ${TEST_NAME} PRIVATE ${CRG_EDITORCONFIG_FILE} ) target_link_libraries( ${TEST_NAME} PUBLIC crg::RenderGraph GTest::gtest fmt::fmt ) target_include_directories( ${TEST_NAME} PUBLIC ${VULKAN_HEADERS_INCLUDE_DIRS} ) set_target_properties( ${TEST_NAME} PROPERTIES CXX_STANDARD 20 FOLDER "Tests/${MAIN_PROJECT_NAME}" ) file( GLOB TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Test*.cpp ) foreach ( TEST_FILE ${TEST_FILES} ) get_filename_component( TEST_NAME ${TEST_FILE} NAME_WE ) add_executable( ${TEST_NAME} ${TEST_FILE} ) target_sources( ${TEST_NAME} PRIVATE ${CRG_EDITORCONFIG_FILE} ) target_link_libraries( ${TEST_NAME} PRIVATE crg::TestCommon ) set_target_properties( ${TEST_NAME} PROPERTIES CXX_STANDARD 20 FOLDER "Tests/${MAIN_PROJECT_NAME}" ) target_compile_definitions( ${TEST_NAME} PRIVATE CRG_TestSuiteName=${TEST_NAME} CRG_TestSuiteNameString="${TEST_NAME}" ) target_include_directories( ${TEST_NAME} PRIVATE ${VULKAN_HEADERS_INCLUDE_DIRS} ) target_add_coverage_flags( ${TEST_NAME} ) if ( NOT CRG_BUILD_STATIC ) if ( WIN32 ) target_link_libraries( ${TEST_NAME} PRIVATE Dbghelp ) else () target_link_libraries( ${TEST_NAME} PRIVATE dl ) endif () endif () if ( MSVC ) target_compile_options( ${TEST_NAME} PRIVATE -bigobj ) endif () if ( PROJECTS_COVERAGE ) coverage_add_target( ${TEST_NAME} MODULES $ $ SOURCES ${CRG_SOURCE_DIR}/include ${CRG_SOURCE_DIR}/source EXCLUDES DotExport ) endif () add_test( NAME ${TEST_NAME} COMMAND ${TEST_NAME} ) install( TARGETS ${TEST_NAME} COMPONENT ${TEST_NAME} EXPORT ${TEST_NAME} RUNTIME DESTINATION bin ) endforeach () if ( PROJECTS_COVERAGE ) coverage_add_merge_target( RenderGraphCoverage ${PROJECTS_DOCUMENTATION_OUTPUT_DIR}/RenderGraphCoverage ) endif () ================================================ FILE: test/Common.cpp ================================================ #include "Common.hpp" #include "BaseTest.hpp" #include #include #include #include #include #include #include #include #include #include namespace test { namespace { void displayTransitions( TestCounts const & testCounts , std::ostream & stream , crg::RunnableGraph const & value , crg::dot::Config cfg ) { cfg.toRemove = testCounts.testName + "/"; crg::dot::displayTransitions( stream, value, cfg ); std::ofstream file{ testCounts.testName + ".dot" }; crg::dot::displayTransitions( file, value, { true, true, true, false, cfg.toRemove } ); } crg::ImageViewType getViewType( crg::ImageType type , crg::ImageCreateFlags flags , uint32_t layerCount ) { switch ( type ) { case crg::ImageType::e1D: return layerCount > 1u ? crg::ImageViewType::e1DArray : crg::ImageViewType::e1D; case crg::ImageType::e3D: return crg::ImageViewType::e3D; default: if ( layerCount > 1u ) { if ( ( ( layerCount % 6u ) == 0u ) && checkFlag( flags, crg::ImageCreateFlags::eCubeCompatible ) ) { return ( layerCount > 6u ) ? crg::ImageViewType::eCubeArray : crg::ImageViewType::eCube; } else { return crg::ImageViewType::e2DArray; } } else { return crg::ImageViewType::e2D; } } } void checkRunnable( TestCounts const & testCounts , crg::RunnableGraph * runnable , std::stringstream & stream ) { require( runnable ) test::display( testCounts, stream, *runnable ); checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) } } crg::BufferData createBuffer( std::string name ) { return crg::BufferData{ std::move( name ) , crg::BufferCreateFlags::eNone , 1024 , ( crg::BufferUsageFlags::eUniformBuffer | crg::BufferUsageFlags::eStorageBuffer | crg::BufferUsageFlags::eUniformTexelBuffer | crg::BufferUsageFlags::eStorageTexelBuffer ) }; } crg::BufferViewData createView( std::string name , crg::BufferId buffer , crg::PixelFormat format ) { return createView( std::move( name ) , buffer , 0u, buffer.data->info.size , format ); } crg::BufferViewData createView( std::string name , crg::BufferId buffer , crg::DeviceSize offset, crg::DeviceSize size , crg::PixelFormat format ) { return crg::BufferViewData{ std::move( name ) , buffer , { offset, size } , format }; } crg::ImageData createImage( std::string name , crg::PixelFormat format , uint32_t mipLevels , uint32_t arrayLayers ) { return crg::ImageData{ std::move( name ) , crg::ImageCreateFlags::eNone , crg::ImageType::e2D , format , { 1024, 1024 } , ( crg::ImageUsageFlags::eColorAttachment | crg::ImageUsageFlags::eSampled ) , mipLevels , arrayLayers }; } crg::ImageData createImage1D( std::string name , crg::PixelFormat format , uint32_t mipLevels , uint32_t arrayLayers ) { return crg::ImageData{ std::move( name ) , crg::ImageCreateFlags::eNone , crg::ImageType::e1D , format , { 1024 } , ( crg::ImageUsageFlags::eColorAttachment | crg::ImageUsageFlags::eSampled ) , mipLevels , arrayLayers }; } crg::ImageData createImage3D( std::string name , crg::PixelFormat format , uint32_t mipLevels , uint32_t arrayLayers ) { return crg::ImageData{ std::move( name ) , crg::ImageCreateFlags::eNone , crg::ImageType::e3D , format , { 1024, 1024u, 64u } , ( crg::ImageUsageFlags::eColorAttachment | crg::ImageUsageFlags::eSampled ) , mipLevels , arrayLayers }; } crg::ImageData createImageCube( std::string name , crg::PixelFormat format , uint32_t mipLevels , uint32_t arrayLayers ) { return crg::ImageData{ std::move( name ) , crg::ImageCreateFlags::eCubeCompatible , crg::ImageType::e2D , format , { 1024, 1024u } , ( crg::ImageUsageFlags::eColorAttachment | crg::ImageUsageFlags::eSampled ) , mipLevels , arrayLayers * 6u }; } crg::ImageViewData createView( std::string name , crg::ImageId image , uint32_t baseMipLevel , uint32_t levelCount , uint32_t baseArrayLayer , uint32_t layerCount ) { return createView( std::move( name ) , image , image.data->info.format , baseMipLevel , levelCount , baseArrayLayer , layerCount ); } crg::ImageViewData createView( std::string name , crg::ImageId image , crg::PixelFormat format , uint32_t baseMipLevel , uint32_t levelCount , uint32_t baseArrayLayer , uint32_t layerCount ) { return crg::ImageViewData{ std::move( name ) , image , crg::ImageViewCreateFlags::eNone , getViewType( getImageType( image ), getImageCreateFlags( image ), layerCount ) , format , { getAspectMask( format ), baseMipLevel, levelCount, baseArrayLayer, layerCount } }; } crg::GraphContext & getDummyContext() { static VkPhysicalDeviceMemoryProperties const MemoryProperties = []() { VkPhysicalDeviceMemoryProperties result{}; // Emulate one device local heap result.memoryHeaps[result.memoryHeapCount++] = { ~0ULL, VK_MEMORY_HEAP_DEVICE_LOCAL_BIT }; // and one host visible heap result.memoryHeaps[result.memoryHeapCount++] = { ~0ULL, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT }; // Emulate few combinations of device local memory types result.memoryTypes[result.memoryTypeCount++] = { VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0u }; result.memoryTypes[result.memoryTypeCount++] = { VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 1u }; result.memoryTypes[result.memoryTypeCount++] = { VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, 1u }; return result; }(); static VkPhysicalDeviceProperties const Properties = []() { std::string name{ "Test" }; VkPhysicalDeviceProperties result{}; #if defined( _MSC_VER ) strncpy_s( result.deviceName , name.c_str() , std::min( name.size() + 1u, size_t( VK_MAX_PHYSICAL_DEVICE_NAME_SIZE - 1u ) ) ); #else strncpy( result.deviceName , name.c_str() , std::min( name.size() + 1u, size_t( VK_MAX_PHYSICAL_DEVICE_NAME_SIZE - 1u ) ) ); #endif result.deviceID = 0u; result.vendorID = 0u; result.driverVersion = VK_MAKE_VERSION( 1, 0, 0 ); result.apiVersion = VK_MAKE_VERSION( 1, 0, 0 ); result.deviceType = VK_PHYSICAL_DEVICE_TYPE_CPU; result.limits.maxImageDimension1D = 16384u; result.limits.maxImageDimension2D = 16384u; result.limits.maxImageDimension3D = 2048u; result.limits.maxImageDimensionCube = 16384u; result.limits.maxImageArrayLayers = 2048u; result.limits.maxTexelBufferElements = 134217728u; result.limits.maxUniformBufferRange = 65536u; result.limits.maxStorageBufferRange = 4294967295u; result.limits.maxPushConstantsSize = 256u; result.limits.maxMemoryAllocationCount = 4096u; result.limits.maxSamplerAllocationCount = 4000u; result.limits.bufferImageGranularity = 1024u; result.limits.sparseAddressSpaceSize = 18446744073709551615u; result.limits.maxBoundDescriptorSets = 8u; result.limits.maxPerStageDescriptorSamplers = 1048576u; result.limits.maxPerStageDescriptorUniformBuffers = 15u; result.limits.maxPerStageDescriptorStorageBuffers = 1048576u; result.limits.maxPerStageDescriptorSampledImages = 1048576u; result.limits.maxPerStageDescriptorStorageImages = 1048576u; result.limits.maxPerStageDescriptorInputAttachments = 1048576u; result.limits.maxPerStageResources = 4294967295u; result.limits.maxDescriptorSetSamplers = 1048576u; result.limits.maxDescriptorSetUniformBuffers = 90u; result.limits.maxDescriptorSetUniformBuffersDynamic = 15u; result.limits.maxDescriptorSetStorageBuffers = 1048576u; result.limits.maxDescriptorSetStorageBuffersDynamic = 16u; result.limits.maxDescriptorSetSampledImages = 1048576u; result.limits.maxDescriptorSetStorageImages = 1048576u; result.limits.maxDescriptorSetInputAttachments = 1048576u; result.limits.maxVertexInputAttributes = 32u; result.limits.maxVertexInputBindings = 32u; result.limits.maxVertexInputAttributeOffset = 2047u; result.limits.maxVertexInputBindingStride = 2048u; result.limits.maxVertexOutputComponents = 128u; result.limits.maxTessellationGenerationLevel = 64u; result.limits.maxTessellationPatchSize = 32u; result.limits.maxTessellationControlPerVertexInputComponents = 128u; result.limits.maxTessellationControlPerVertexOutputComponents = 128u; result.limits.maxTessellationControlPerPatchOutputComponents = 120u; result.limits.maxTessellationControlTotalOutputComponents = 4216u; result.limits.maxTessellationEvaluationInputComponents = 128u; result.limits.maxTessellationEvaluationOutputComponents = 128u; result.limits.maxGeometryShaderInvocations = 32u; result.limits.maxGeometryInputComponents = 128u; result.limits.maxGeometryOutputComponents = 128u; result.limits.maxGeometryOutputVertices = 1024u; result.limits.maxGeometryTotalOutputComponents = 1024u; result.limits.maxFragmentInputComponents = 128u; result.limits.maxFragmentOutputAttachments = 8u; result.limits.maxFragmentDualSrcAttachments = 1u; result.limits.maxFragmentCombinedOutputResources = 16u; result.limits.maxComputeSharedMemorySize = 49152u; result.limits.maxComputeWorkGroupCount[0] = 2147483647u; result.limits.maxComputeWorkGroupCount[1] = 65535u; result.limits.maxComputeWorkGroupCount[2] = 65535u; result.limits.maxComputeWorkGroupInvocations = 1536u; result.limits.maxComputeWorkGroupSize[0] = 1536u; result.limits.maxComputeWorkGroupSize[1] = 1024u; result.limits.maxComputeWorkGroupSize[2] = 64u; result.limits.subPixelPrecisionBits = 8u; result.limits.subTexelPrecisionBits = 8u; result.limits.mipmapPrecisionBits = 8u; result.limits.maxDrawIndexedIndexValue = 4294967295u; result.limits.maxDrawIndirectCount = 4294967295u; result.limits.maxSamplerLodBias = 15.0f; result.limits.maxSamplerAnisotropy = 16.0f; result.limits.maxViewports = 16u; result.limits.maxViewportDimensions[0] = 16384u; result.limits.maxViewportDimensions[1] = 16384u; result.limits.viewportBoundsRange[0] = -32768.0f; result.limits.viewportBoundsRange[1] = 32768.0f; result.limits.viewportSubPixelBits = 8u; result.limits.minMemoryMapAlignment = 64u; result.limits.minTexelBufferOffsetAlignment = 16u; result.limits.minUniformBufferOffsetAlignment = 256u; result.limits.minStorageBufferOffsetAlignment = 32u; result.limits.minTexelOffset = -8; result.limits.maxTexelOffset = 7u; result.limits.minTexelGatherOffset = -32; result.limits.maxTexelGatherOffset = 31u; result.limits.minInterpolationOffset = -0.5f; result.limits.maxInterpolationOffset = 0.4375f; result.limits.subPixelInterpolationOffsetBits = 4u; result.limits.maxFramebufferWidth = 16384u; result.limits.maxFramebufferHeight = 16384u; result.limits.maxFramebufferLayers = 2048u; result.limits.framebufferColorSampleCounts = 15u; result.limits.framebufferDepthSampleCounts = 15u; result.limits.framebufferStencilSampleCounts = 31u; result.limits.framebufferNoAttachmentsSampleCounts = 31u; result.limits.maxColorAttachments = 8u; result.limits.sampledImageColorSampleCounts = 15u; result.limits.sampledImageIntegerSampleCounts = 15u; result.limits.sampledImageDepthSampleCounts = 15u; result.limits.sampledImageStencilSampleCounts = 31u; result.limits.storageImageSampleCounts = 15u; result.limits.maxSampleMaskWords = 1u; result.limits.timestampComputeAndGraphics = true; result.limits.timestampPeriod = 1.0f; result.limits.maxClipDistances = 8u; result.limits.maxCullDistances = 8u; result.limits.maxCombinedClipAndCullDistances = 8u; result.limits.discreteQueuePriorities = 2u; result.limits.pointSizeRange[0] = 1.0f; result.limits.pointSizeRange[1] = 189.875f; result.limits.lineWidthRange[0] = 0.5f; result.limits.lineWidthRange[1] = 10.0f; result.limits.pointSizeGranularity = 0.125f; result.limits.lineWidthGranularity = 0.125f; result.limits.strictLines = true; result.limits.standardSampleLocations = true; result.limits.optimalBufferCopyOffsetAlignment = 1u; result.limits.optimalBufferCopyRowPitchAlignment = 1u; result.limits.nonCoherentAtomSize = 64ULL; result.sparseProperties.residencyAlignedMipSize = true; result.sparseProperties.residencyNonResidentStrict = true; result.sparseProperties.residencyStandard2DBlockShape = true; result.sparseProperties.residencyStandard2DMultisampleBlockShape = true; result.sparseProperties.residencyStandard3DBlockShape = true; return result; }(); static crg::GraphContext context{ nullptr , nullptr , nullptr , MemoryProperties , Properties , false , nullptr }; static std::atomic< uintptr_t > counter{}; context.device = VkDevice( counter.load() ); ++counter; context.vkCreateGraphicsPipelines = PFN_vkCreateGraphicsPipelines( []( VkDevice, VkPipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo *, const VkAllocationCallbacks *, VkPipeline * pPipelines ) { for ( uint32_t i = 0u; i < createInfoCount; ++i ) { pPipelines[i] = VkPipeline( counter.load() ); ++counter; } return VK_SUCCESS; } ); context.vkCreateComputePipelines = PFN_vkCreateComputePipelines( []( VkDevice, VkPipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo *, const VkAllocationCallbacks *, VkPipeline * pPipelines ) { for ( uint32_t i = 0u; i < createInfoCount; ++i ) { pPipelines[i] = VkPipeline( counter.load() ); ++counter; } return VK_SUCCESS; } ); context.vkCreatePipelineLayout = PFN_vkCreatePipelineLayout( []( VkDevice, const VkPipelineLayoutCreateInfo *, const VkAllocationCallbacks *, VkPipelineLayout * pPipelineLayout ) { *pPipelineLayout = VkPipelineLayout( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateDescriptorSetLayout = PFN_vkCreateDescriptorSetLayout( []( VkDevice, const VkDescriptorSetLayoutCreateInfo *, const VkAllocationCallbacks *, VkDescriptorSetLayout * pSetLayout ) { *pSetLayout = VkDescriptorSetLayout( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateDescriptorPool = PFN_vkCreateDescriptorPool( []( VkDevice, const VkDescriptorPoolCreateInfo *, const VkAllocationCallbacks *, VkDescriptorPool * pDescriptorPool ) { *pDescriptorPool = VkDescriptorPool( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkAllocateDescriptorSets = PFN_vkAllocateDescriptorSets( []( VkDevice, const VkDescriptorSetAllocateInfo * pAllocateInfo, VkDescriptorSet * pDescriptorSets ) { if ( !pAllocateInfo ) return VK_ERROR_UNKNOWN; for ( uint32_t i = 0u; i < pAllocateInfo->descriptorSetCount; ++i ) { pDescriptorSets[i] = VkDescriptorSet( counter.load() ); ++counter; } return VK_SUCCESS; } ); context.vkCreateBuffer = PFN_vkCreateBuffer( []( VkDevice, const VkBufferCreateInfo *, const VkAllocationCallbacks *, VkBuffer * pBuffer ) { *pBuffer = VkBuffer( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateBufferView = PFN_vkCreateBufferView( []( VkDevice, const VkBufferViewCreateInfo *, const VkAllocationCallbacks *, VkBufferView * pBuffer ) { *pBuffer = VkBufferView( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkAllocateMemory = PFN_vkAllocateMemory( []( VkDevice, const VkMemoryAllocateInfo *, const VkAllocationCallbacks *, VkDeviceMemory * pMemory ) { *pMemory = VkDeviceMemory( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateRenderPass = PFN_vkCreateRenderPass( []( VkDevice, const VkRenderPassCreateInfo *, const VkAllocationCallbacks *, VkRenderPass * pRenderPass ) { *pRenderPass = VkRenderPass( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateFramebuffer = PFN_vkCreateFramebuffer( []( VkDevice, const VkFramebufferCreateInfo *, const VkAllocationCallbacks *, VkFramebuffer * pFramebuffer ) { *pFramebuffer = VkFramebuffer( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateImage = PFN_vkCreateImage( []( VkDevice, const VkImageCreateInfo *, const VkAllocationCallbacks *, VkImage * pImage ) { *pImage = VkImage( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateImageView = PFN_vkCreateImageView( []( VkDevice, const VkImageViewCreateInfo *, const VkAllocationCallbacks *, VkImageView * pView ) { *pView = VkImageView( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateSampler = PFN_vkCreateSampler( []( VkDevice, const VkSamplerCreateInfo *, const VkAllocationCallbacks *, VkSampler * pSampler ) { *pSampler = VkSampler( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateCommandPool = PFN_vkCreateCommandPool( []( VkDevice, const VkCommandPoolCreateInfo *, const VkAllocationCallbacks *, VkCommandPool * pCommandPool ) { *pCommandPool = VkCommandPool( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkAllocateCommandBuffers = PFN_vkAllocateCommandBuffers( []( VkDevice, const VkCommandBufferAllocateInfo * pAllocateInfo, VkCommandBuffer * pCommandBuffers ) { if ( !pAllocateInfo ) return VK_ERROR_UNKNOWN; for ( uint32_t i = 0u; i < pAllocateInfo->commandBufferCount; ++i ) { pCommandBuffers[i] = VkCommandBuffer( counter.load() ); ++counter; } return VK_SUCCESS; } ); context.vkCreateSemaphore = PFN_vkCreateSemaphore( []( VkDevice, const VkSemaphoreCreateInfo *, const VkAllocationCallbacks *, VkSemaphore * pSemaphore ) { *pSemaphore = VkSemaphore( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateQueryPool = PFN_vkCreateQueryPool( []( VkDevice, const VkQueryPoolCreateInfo *, const VkAllocationCallbacks *, VkQueryPool * pQueryPool ) { *pQueryPool = VkQueryPool( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateEvent = PFN_vkCreateEvent( []( VkDevice, const VkEventCreateInfo *, const VkAllocationCallbacks *, VkEvent * pEvent ) { *pEvent = VkEvent( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkCreateFence = PFN_vkCreateFence( []( VkDevice, const VkFenceCreateInfo *, const VkAllocationCallbacks *, VkFence * pFence ) { *pFence = VkFence( counter.load() ); ++counter; return VK_SUCCESS; } ); context.vkDestroyPipeline = PFN_vkDestroyPipeline( []( VkDevice, VkPipeline, const VkAllocationCallbacks * ){} ); context.vkDestroyPipelineLayout = PFN_vkDestroyPipelineLayout( []( VkDevice, VkPipelineLayout, const VkAllocationCallbacks * ){} ); context.vkDestroyDescriptorSetLayout = PFN_vkDestroyDescriptorSetLayout( []( VkDevice, VkDescriptorSetLayout, const VkAllocationCallbacks* ){} ); context.vkDestroyDescriptorPool = PFN_vkDestroyDescriptorPool( []( VkDevice, VkDescriptorPool, const VkAllocationCallbacks * ){} ); context.vkFreeDescriptorSets = PFN_vkFreeDescriptorSets( []( VkDevice, VkDescriptorPool, uint32_t, const VkDescriptorSet * ){ return VK_SUCCESS; } ); context.vkDestroyBuffer = PFN_vkDestroyBuffer( []( VkDevice, VkBuffer, const VkAllocationCallbacks * ){} ); context.vkDestroyBufferView = PFN_vkDestroyBufferView( []( VkDevice, VkBufferView, const VkAllocationCallbacks * ){} ); context.vkFreeMemory = PFN_vkFreeMemory( []( VkDevice, VkDeviceMemory, const VkAllocationCallbacks * ){} ); context.vkDestroyRenderPass = PFN_vkDestroyRenderPass( []( VkDevice, VkRenderPass, const VkAllocationCallbacks * ){} ); context.vkDestroyFramebuffer = PFN_vkDestroyFramebuffer( []( VkDevice, VkFramebuffer, const VkAllocationCallbacks * ){} ); context.vkDestroyImage = PFN_vkDestroyImage( []( VkDevice, VkImage, const VkAllocationCallbacks * ){} ); context.vkDestroyImageView = PFN_vkDestroyImageView( []( VkDevice, VkImageView, const VkAllocationCallbacks * ){} ); context.vkDestroySampler = PFN_vkDestroySampler( []( VkDevice, VkSampler, const VkAllocationCallbacks * ){} ); context.vkDestroyCommandPool = PFN_vkDestroyCommandPool( []( VkDevice, VkCommandPool, const VkAllocationCallbacks * ){} ); context.vkFreeCommandBuffers = PFN_vkFreeCommandBuffers( []( VkDevice, VkCommandPool, uint32_t, const VkCommandBuffer * ){} ); context.vkDestroySemaphore = PFN_vkDestroySemaphore( []( VkDevice, VkSemaphore, const VkAllocationCallbacks * ){} ); context.vkDestroyQueryPool = PFN_vkDestroyQueryPool( []( VkDevice, VkQueryPool, const VkAllocationCallbacks * ){} ); context.vkDestroyEvent = PFN_vkDestroyEvent( []( VkDevice, VkEvent, const VkAllocationCallbacks * ){} ); context.vkDestroyFence = PFN_vkDestroyFence( []( VkDevice, VkFence, const VkAllocationCallbacks * ){} ); context.vkGetBufferMemoryRequirements = PFN_vkGetBufferMemoryRequirements( []( VkDevice, VkBuffer, VkMemoryRequirements * pMemoryRequirements ) { pMemoryRequirements->memoryTypeBits = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; pMemoryRequirements->size = 1024u; pMemoryRequirements->alignment = 1u; } ); context.vkGetImageMemoryRequirements = PFN_vkGetImageMemoryRequirements( []( VkDevice, VkImage, VkMemoryRequirements * pMemoryRequirements ) { pMemoryRequirements->memoryTypeBits = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; pMemoryRequirements->size = 1024u * 1024 * 64; pMemoryRequirements->alignment = 4u; } ); context.vkBindBufferMemory = PFN_vkBindBufferMemory( []( VkDevice, VkBuffer, VkDeviceMemory, VkDeviceSize ){ return VK_SUCCESS; } ); context.vkBindImageMemory = PFN_vkBindImageMemory( []( VkDevice, VkImage, VkDeviceMemory, VkDeviceSize ){ return VK_SUCCESS; } ); context.vkMapMemory = PFN_vkMapMemory( []( VkDevice, VkDeviceMemory, VkDeviceSize, VkDeviceSize, VkMemoryMapFlags, void ** ppData ) { thread_local std::vector< uint8_t > data( 1024u * 1024u * 64u ); *ppData = data.data(); return VK_SUCCESS; } ); context.vkUnmapMemory = PFN_vkUnmapMemory( []( VkDevice, VkDeviceMemory ){} ); context.vkFlushMappedMemoryRanges = PFN_vkFlushMappedMemoryRanges( []( VkDevice, uint32_t , const VkMappedMemoryRange * ){ return VK_SUCCESS; } ); context.vkInvalidateMappedMemoryRanges = PFN_vkInvalidateMappedMemoryRanges( []( VkDevice, uint32_t , const VkMappedMemoryRange * ){ return VK_SUCCESS; } ); context.vkUpdateDescriptorSets = PFN_vkUpdateDescriptorSets( []( VkDevice, uint32_t, const VkWriteDescriptorSet *, uint32_t, const VkCopyDescriptorSet * ){} ); context.vkBeginCommandBuffer = PFN_vkBeginCommandBuffer( []( VkCommandBuffer, const VkCommandBufferBeginInfo * ){ return VK_SUCCESS; } ); context.vkEndCommandBuffer = PFN_vkEndCommandBuffer( []( VkCommandBuffer ){ return VK_SUCCESS; } ); context.vkQueueSubmit = PFN_vkQueueSubmit( []( VkQueue, uint32_t, const VkSubmitInfo *, VkFence ){ return VK_SUCCESS; } ); context.vkGetQueryPoolResults = PFN_vkGetQueryPoolResults( []( VkDevice, VkQueryPool, uint32_t, uint32_t, size_t, void *, VkDeviceSize, VkQueryResultFlags ){ return VK_SUCCESS; } ); context.vkResetCommandBuffer = PFN_vkResetCommandBuffer( []( VkCommandBuffer, VkCommandBufferResetFlags ){ return VK_SUCCESS; } ); context.vkResetEvent = PFN_vkResetEvent( []( VkDevice, VkEvent ){ return VK_SUCCESS; } ); context.vkSetEvent = PFN_vkSetEvent( []( VkDevice, VkEvent ){ return VK_SUCCESS; } ); context.vkGetEventStatus = PFN_vkGetEventStatus( []( VkDevice, VkEvent ){ return VK_SUCCESS; } ); context.vkGetFenceStatus = PFN_vkGetFenceStatus( []( VkDevice, VkFence ){ return VK_SUCCESS; } ); context.vkWaitForFences = PFN_vkWaitForFences( []( VkDevice, uint32_t, const VkFence *, VkBool32, uint64_t ){ return VK_SUCCESS; } ); context.vkResetFences = PFN_vkResetFences( []( VkDevice, uint32_t, const VkFence * ){ return VK_SUCCESS; } ); context.vkCmdBindPipeline = PFN_vkCmdBindPipeline( []( VkCommandBuffer, VkPipelineBindPoint, VkPipeline ){} ); context.vkCmdBindDescriptorSets = PFN_vkCmdBindDescriptorSets( []( VkCommandBuffer, VkPipelineBindPoint, VkPipelineLayout, uint32_t, uint32_t, const VkDescriptorSet *, uint32_t, const uint32_t * ){} ); context.vkCmdBindVertexBuffers = PFN_vkCmdBindVertexBuffers( []( VkCommandBuffer, uint32_t, uint32_t, const VkBuffer *, const VkDeviceSize * ){} ); context.vkCmdBindIndexBuffer = PFN_vkCmdBindIndexBuffer( []( VkCommandBuffer, VkBuffer, VkDeviceSize, VkIndexType ){} ); context.vkCmdClearColorImage = PFN_vkCmdClearColorImage( []( VkCommandBuffer, VkImage, VkImageLayout, const VkClearColorValue *, uint32_t, const VkImageSubresourceRange * ){} ); context.vkCmdClearDepthStencilImage = PFN_vkCmdClearDepthStencilImage( []( VkCommandBuffer, VkImage, VkImageLayout, const VkClearDepthStencilValue *, uint32_t, const VkImageSubresourceRange * ){} ); context.vkCmdDispatch = PFN_vkCmdDispatch( []( VkCommandBuffer, uint32_t, uint32_t, uint32_t ){} ); context.vkCmdDispatchIndirect = PFN_vkCmdDispatchIndirect( []( VkCommandBuffer, VkBuffer, VkDeviceSize ){} ); context.vkCmdDraw = PFN_vkCmdDraw( []( VkCommandBuffer, uint32_t, uint32_t, uint32_t, uint32_t ){} ); context.vkCmdDrawIndexed = PFN_vkCmdDrawIndexed( []( VkCommandBuffer, uint32_t, uint32_t, uint32_t, int32_t, uint32_t ){} ); context.vkCmdDrawIndexedIndirect = PFN_vkCmdDrawIndexedIndirect( []( VkCommandBuffer, VkBuffer, VkDeviceSize, uint32_t, uint32_t ){} ); context.vkCmdDrawIndirect = PFN_vkCmdDrawIndirect( []( VkCommandBuffer, VkBuffer, VkDeviceSize, uint32_t, uint32_t ){} ); context.vkCmdBeginRenderPass = PFN_vkCmdBeginRenderPass( []( VkCommandBuffer, const VkRenderPassBeginInfo *, VkSubpassContents ){} ); context.vkCmdEndRenderPass = PFN_vkCmdEndRenderPass( []( VkCommandBuffer ){} ); context.vkCmdPushConstants = PFN_vkCmdPushConstants( []( VkCommandBuffer, VkPipelineLayout, VkShaderStageFlags, uint32_t, uint32_t, const void * ){} ); context.vkCmdResetQueryPool = PFN_vkCmdResetQueryPool( []( VkCommandBuffer, VkQueryPool, uint32_t, uint32_t ){} ); context.vkCmdWriteTimestamp = PFN_vkCmdWriteTimestamp( []( VkCommandBuffer, VkPipelineStageFlagBits, VkQueryPool, uint32_t ){} ); context.vkCmdPipelineBarrier = PFN_vkCmdPipelineBarrier( []( VkCommandBuffer, VkPipelineStageFlags, VkPipelineStageFlags, VkDependencyFlags, uint32_t, const VkMemoryBarrier *, uint32_t, const VkBufferMemoryBarrier *, uint32_t, const VkImageMemoryBarrier * ){} ); context.vkCmdBlitImage = PFN_vkCmdBlitImage( []( VkCommandBuffer, VkImage, VkImageLayout, VkImage, VkImageLayout, uint32_t, const VkImageBlit *, VkFilter ){} ); context.vkCmdCopyBuffer = PFN_vkCmdCopyBuffer( []( VkCommandBuffer, VkBuffer, VkBuffer, uint32_t, const VkBufferCopy * ){} ); context.vkCmdCopyBufferToImage = PFN_vkCmdCopyBufferToImage( []( VkCommandBuffer, VkBuffer, VkImage, VkImageLayout, uint32_t, const VkBufferImageCopy * ){} ); context.vkCmdCopyImage = PFN_vkCmdCopyImage( []( VkCommandBuffer, VkImage, VkImageLayout, VkImage, VkImageLayout, uint32_t, const VkImageCopy * ){} ); context.vkCmdCopyImageToBuffer = PFN_vkCmdCopyImageToBuffer( []( VkCommandBuffer, VkImage, VkImageLayout, VkBuffer, uint32_t, const VkBufferImageCopy * ){} ); context.vkCmdExecuteCommands = PFN_vkCmdExecuteCommands( []( VkCommandBuffer, uint32_t, const VkCommandBuffer * ){} ); context.vkCmdResetEvent = PFN_vkCmdResetEvent( []( VkCommandBuffer, VkEvent, VkPipelineStageFlags ){} ); context.vkCmdSetEvent = PFN_vkCmdSetEvent( []( VkCommandBuffer, VkEvent, VkPipelineStageFlags ){} ); context.vkCmdWaitEvents = PFN_vkCmdWaitEvents( []( VkCommandBuffer, uint32_t, const VkEvent *, VkPipelineStageFlags, VkPipelineStageFlags, uint32_t, const VkMemoryBarrier *, uint32_t, const VkBufferMemoryBarrier *, uint32_t, const VkImageMemoryBarrier * ){} ); context.vkCmdFillBuffer = PFN_vkCmdFillBuffer( []( VkCommandBuffer, VkBuffer, VkDeviceSize, VkDeviceSize, uint32_t ){} ); #if VK_EXT_debug_utils || VK_EXT_debug_marker # if VK_EXT_debug_utils context.vkSetDebugUtilsObjectNameEXT = PFN_vkSetDebugUtilsObjectNameEXT( []( VkDevice, const VkDebugUtilsObjectNameInfoEXT* ){ return VK_SUCCESS; } ); context.vkCmdBeginDebugUtilsLabelEXT = PFN_vkCmdBeginDebugUtilsLabelEXT( []( VkCommandBuffer, const VkDebugUtilsLabelEXT * ){} ); context.vkCmdEndDebugUtilsLabelEXT = PFN_vkCmdEndDebugUtilsLabelEXT( []( VkCommandBuffer ){} ); # endif # if VK_EXT_debug_marker context.vkDebugMarkerSetObjectNameEXT = PFN_vkDebugMarkerSetObjectNameEXT( []( VkDevice, const VkDebugMarkerObjectNameInfoEXT * ){ return VK_SUCCESS; } ); context.vkCmdDebugMarkerBeginEXT = PFN_vkCmdDebugMarkerBeginEXT( []( VkCommandBuffer, const VkDebugMarkerMarkerInfoEXT * ){} ); context.vkCmdDebugMarkerEndEXT = PFN_vkCmdDebugMarkerEndEXT( []( VkCommandBuffer ){} ); # endif #endif context.setCallstackCallback( []() { return "coin !!"; } ); return context; } std::string checkRunnable( TestCounts const & testCounts , crg::RunnableGraph * runnable ) { std::stringstream result; checkRunnable( testCounts, runnable, result ); return result.str(); } void display( TestCounts const & testCounts , std::ostream & stream , crg::RunnableGraph const & value , bool withColours , bool withIds , bool withGroups ) { std::stringstream tmp; crg::dot::displayTransitions( tmp, value, { true, true, true, true } ); crg::dot::displayTransitions( tmp, value, { true, true, true, false } ); crg::dot::displayTransitions( tmp, value, { true, true, false, true } ); crg::dot::displayTransitions( tmp, value, { true, true, false, false } ); crg::dot::displayTransitions( tmp, value, { true, false, true, true } ); crg::dot::displayTransitions( tmp, value, { true, false, true, false } ); crg::dot::displayTransitions( tmp, value, { true, false, false, true } ); crg::dot::displayTransitions( tmp, value, { true, false, false, false } ); crg::dot::displayTransitions( tmp, value, { false, true, true, true } ); crg::dot::displayTransitions( tmp, value, { false, true, true, false } ); crg::dot::displayTransitions( tmp, value, { false, true, false, true } ); crg::dot::displayTransitions( tmp, value, { false, true, false, false } ); crg::dot::displayTransitions( tmp, value, { false, false, true, true } ); crg::dot::displayTransitions( tmp, value, { false, false, true, false } ); crg::dot::displayTransitions( tmp, value, { false, false, false, true } ); crg::dot::displayTransitions( tmp, value, { false, false, false, false } ); displayTransitions( testCounts, stream, value, { withColours, withIds, withGroups } ); } void display( TestCounts const & testCounts , crg::RunnableGraph const & value , bool withColours , bool withIds , bool withGroups ) { display( testCounts, std::cout, value, withColours, withIds, withGroups ); } class DummyRunnable : public crg::RunnablePass { public: DummyRunnable( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , test::TestCounts & testCounts , crg::PipelineStageFlags pipelineStageFlags , CheckViews checkViews , uint32_t index , bool enabled , crg::ru::Config config ) : crg::RunnablePass{ framePass , context , runGraph , { crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::RunnablePass::GetPipelineStateCallback( [this](){ return crg::getPipelineState( m_pipelineStageFlags ); } ) , crg::RunnablePass::RecordCallback( [this]( crg::RecordContext & ctx, VkCommandBuffer cb, uint32_t i ){ doRecordInto( ctx, cb, i ); } ) , crg::RunnablePass::GetPassIndexCallback( [index](){ return index; } ) , crg::RunnablePass::IsEnabledCallback( [enabled](){ return enabled; } ) } , std::move( config ) } , m_testCounts{ testCounts } , m_pipelineStageFlags{ pipelineStageFlags } , m_checkViews{ std::move( checkViews ) } { } DummyRunnable( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , test::TestCounts & testCounts , crg::PipelineStageFlags pipelineStageFlags , CheckViews checkViews , uint32_t index , crg::ru::Config config ) : crg::RunnablePass{ framePass , context , runGraph , { crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::RunnablePass::GetPipelineStateCallback( [this](){ return crg::getPipelineState( m_pipelineStageFlags ); } ) , crg::RunnablePass::RecordCallback( [this]( crg::RecordContext & ctx, VkCommandBuffer cb, uint32_t i ){ doRecordInto( ctx, cb, i ); } ) , crg::RunnablePass::GetPassIndexCallback( [index](){ return index; } ) } , std::move( config ) } , m_testCounts{ testCounts } , m_pipelineStageFlags{ pipelineStageFlags } , m_checkViews{ std::move( checkViews ) } { } DummyRunnable( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , test::TestCounts & testCounts , crg::PipelineStageFlags pipelineStageFlags , CheckViews checkViews , crg::ru::Config config ) : crg::RunnablePass{ framePass , context , runGraph , { crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::RunnablePass::GetPipelineStateCallback( [this](){ return crg::getPipelineState( m_pipelineStageFlags ); } ) , crg::RunnablePass::RecordCallback( [this]( crg::RecordContext & ctx, VkCommandBuffer cb, uint32_t i ){ doRecordInto( ctx, cb, i ); } ) } , std::move( config ) } , m_testCounts{ testCounts } , m_pipelineStageFlags{ pipelineStageFlags } , m_checkViews{ std::move( checkViews ) } { } DummyRunnable( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , test::TestCounts & testCounts , crg::PipelineStageFlags pipelineStageFlags , crg::ru::Config config ) : crg::RunnablePass{ framePass , context , runGraph , { crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::RunnablePass::GetPipelineStateCallback( [this](){ return crg::getPipelineState( m_pipelineStageFlags ); } ) , crg::RunnablePass::RecordCallback( [this]( crg::RecordContext & ctx, VkCommandBuffer cb, uint32_t i ){ doRecordTargetsInto( ctx, cb, i ); } ) } , std::move( config ) } , m_testCounts{ testCounts } , m_pipelineStageFlags{ pipelineStageFlags } { } private: void doRecordInto( crg::RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index ) { for ( auto & [binding, attach] : getPass().getUniforms() ) { auto buffer = crg::resolveView( attach->buffer( index ), index ); context.setAccessState( buffer , { attach->getAccessMask(), attach->getPipelineStageFlags( checkFlag( m_pipelineStageFlags, crg::PipelineStageFlags::eComputeShader ) ) } ); } for ( auto & [binding, attach] : getPass().getSampled() ) { auto view = attach.attach->view( index ); context.runImplicitTransition( commandBuffer, index, view ); context.setLayoutState( crg::resolveView( view, index ) , crg::makeLayoutState( attach.attach->getImageLayout( context->separateDepthStencilLayouts ) ) ); } for ( auto & [binding, attach] : getPass().getInputs() ) { if ( attach->isImage() ) { auto view = attach->view( index ); context.runImplicitTransition( commandBuffer, index, view ); context.setLayoutState( crg::resolveView( view, index ) , crg::makeLayoutState( attach->getImageLayout( context->separateDepthStencilLayouts ) ) ); } else { auto buffer = crg::resolveView( attach->buffer( index ), index ); context.setAccessState( buffer , { attach->getAccessMask(), attach->getPipelineStageFlags( checkFlag( m_pipelineStageFlags, crg::PipelineStageFlags::eComputeShader ) ) } ); } } for ( auto & [binding, attach] : getPass().getInouts() ) { if ( attach->isImage() ) { auto view = attach->view( index ); context.runImplicitTransition( commandBuffer, index, view ); context.setLayoutState( crg::resolveView( view, index ) , crg::makeLayoutState( attach->getImageLayout( context->separateDepthStencilLayouts ) ) ); } else { auto buffer = crg::resolveView( attach->buffer( index ), index ); context.setAccessState( buffer , { attach->getAccessMask(), attach->getPipelineStageFlags( checkFlag( m_pipelineStageFlags, crg::PipelineStageFlags::eComputeShader ) ) } ); } } for ( auto & [binding, attach] : getPass().getOutputs() ) { if ( attach->isImage() ) { auto view = attach->view( index ); context.runImplicitTransition( commandBuffer, index, view ); context.setLayoutState( crg::resolveView( view, index ) , crg::makeLayoutState( attach->getImageLayout( context->separateDepthStencilLayouts ) ) ); } else { auto buffer = crg::resolveView( attach->buffer( index ), index ); context.setAccessState( buffer , { attach->getAccessMask(), attach->getPipelineStageFlags( checkFlag( m_pipelineStageFlags, crg::PipelineStageFlags::eComputeShader ) ) } ); } } for ( auto attach : getPass().getTargets() ) { auto view = attach->view( index ); context.runImplicitTransition( commandBuffer, index, view ); context.setLayoutState( crg::resolveView( view, index ) , crg::makeLayoutState( attach->getImageLayout( context->separateDepthStencilLayouts ) ) ); } m_checkViews( m_testCounts , getPass() , getGraph() , context , index ); } void doRecordTargetsInto( crg::RecordContext & context , VkCommandBuffer commandBuffer , uint32_t index )const { for ( auto attach : getPass().getTargets() ) { auto view = attach->view( index ); context.runImplicitTransition( commandBuffer, index, view ); context.setLayoutState( crg::resolveView( view, index ) , crg::makeLayoutState( attach->getImageLayout( context->separateDepthStencilLayouts ) ) ); } } test::TestCounts & m_testCounts; crg::PipelineStageFlags m_pipelineStageFlags; CheckViews m_checkViews; }; class DummyRunnableNoRecord : public crg::RunnablePass { public: DummyRunnableNoRecord( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , crg::ru::Config config ) : crg::RunnablePass{ framePass , context , runGraph , { crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::RunnablePass::GetPipelineStateCallback( [this](){ return crg::getPipelineState( m_pipelineStageFlags ); } ) } , std::move( config ) } , m_pipelineStageFlags{ pipelineStageFlags } { } crg::PipelineStageFlags m_pipelineStageFlags; }; crg::RunnablePassPtr createDummy( test::TestCounts & testCounts , crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , CheckViews checkViews , uint32_t index , bool enabled , crg::ru::Config config ) { return std::make_unique< DummyRunnable >( framePass , context , runGraph , testCounts , pipelineStageFlags , checkViews , index , enabled , std::move( config ) ); } crg::RunnablePassPtr createDummy( test::TestCounts & testCounts , crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , CheckViews checkViews , uint32_t index , crg::ru::Config config ) { return std::make_unique< DummyRunnable >( framePass , context , runGraph , testCounts , pipelineStageFlags , checkViews , index , std::move( config ) ); } crg::RunnablePassPtr createDummy( test::TestCounts & testCounts , crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , CheckViews checkViews , crg::ru::Config config ) { return std::make_unique< DummyRunnable >( framePass , context , runGraph , testCounts , pipelineStageFlags , std::move( checkViews ) , std::move( config ) ); } crg::RunnablePassPtr createDummy( test::TestCounts & testCounts , crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , crg::ru::Config config ) { return std::make_unique< DummyRunnable >( framePass , context , runGraph , testCounts , pipelineStageFlags , std::move( config ) ); } crg::RunnablePassPtr createDummyNoRecord( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , crg::ru::Config config ) { return std::make_unique< DummyRunnableNoRecord >( framePass , context , runGraph , pipelineStageFlags , std::move( config ) ); } void checkDummy( [[maybe_unused]] test::TestCounts const & testCounts , [[maybe_unused]] crg::FramePass const & framePass , [[maybe_unused]] crg::RunnableGraph const & graph , [[maybe_unused]] crg::RecordContext const & context , [[maybe_unused]] uint32_t index ) { // Nothing checked yet... } template< typename EnumT > static void condAppendEnumFlag( std::ostream & stream, std::string & sep, EnumT const & v, EnumT const & t, std::string_view s ) { if ( checkFlag( v, t ) ) { stream << sep << s; sep = " | "; } } } namespace crg { std::ostream & operator<<( std::ostream & stream, AccessFlags const & v ) { std::string sep; test::condAppendEnumFlag( stream, sep, v, AccessFlags::eIndirectCommandRead, "IndirectCommandRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eIndexRead, "IndexRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eVertexAttributeRead, "VertexAttributeRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eUniformRead, "UniformRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eInputAttachmentRead, "InputAttachmentRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eShaderRead, "ShaderRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eShaderWrite, "ShaderWrite" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eColorAttachmentRead, "ColorAttachmentRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eColorAttachmentWrite, "ColorAttachmentWrite" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eDepthStencilAttachmentRead, "DepthStencilAttachmentRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eDepthStencilAttachmentWrite, "DepthStencilAttachmentWrite" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eTransferRead, "TransferRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eTransferWrite, "TransferWrite" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eHostRead, "HostRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eHostWrite, "HostWrite" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eMemoryRead, "MemoryRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eMemoryWrite, "MemoryWrite" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eTransformFeedbackWrite, "TransformFeedbackWrite" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eTransformFeedbackCounterRead, "TransformFeedbackCounterRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eTransformFeedbackCounterWrite, "TransformFeedbackCounterWrite" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eConditionalRenderingRead, "ConditionalRenderingRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eColorAttachmentReadNonCoherent, "ColorAttachmentReadNonCoherent" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eAccelerationStructureRead, "AccelerationStructureRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eAccelerationStructureWrite, "AccelerationStructureWrite" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eFragmentDensityMapRead, "FragmentDensityMapRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eFragmentShadingRateAttachmentRead, "FragmentShadingRateAttachmentRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eCommandPreprocessRead, "CommandPreprocessRead" ); test::condAppendEnumFlag( stream, sep, v, AccessFlags::eCommandPreprocessWrite, "CommandPreprocessWrite" ); return stream; } std::ostream & operator<<( std::ostream & stream, ImageLayout const & v ) { switch ( v ) { case ImageLayout::eUndefined: stream << "Undefined"; break; case ImageLayout::eGeneral: stream << "General"; break; case ImageLayout::eColorAttachment: stream << "ColorAttachment"; break; case ImageLayout::eDepthStencilAttachment: stream << "DepthStencilAttachment"; break; case ImageLayout::eDepthStencilReadOnly: stream << "DepthStencilReadOnly"; break; case ImageLayout::eShaderReadOnly: stream << "ShaderReadOnly"; break; case ImageLayout::eTransferSrc: stream << "TransferSrc"; break; case ImageLayout::eTransferDst: stream << "TransferDst"; break; case ImageLayout::ePreinitialized: stream << "Preinitialized"; break; case ImageLayout::eDepthReadOnlyStencilAttachment: stream << "DepthReadOnlyStencilAttachment"; break; case ImageLayout::eDepthAttachmentStencilReadOnly: stream << "DepthAttachmentStencilReadOnly"; break; case ImageLayout::eDepthAttachment: stream << "DepthAttachment"; break; case ImageLayout::eDepthReadOnly: stream << "DepthReadOnly"; break; case ImageLayout::eStencilAttachment: stream << "StencilAttachment"; break; case ImageLayout::eStencilReadOnly: stream << "StencilReadOnly"; break; case ImageLayout::eReadOnly: stream << "ReadOnly"; break; case ImageLayout::eAttachment: stream << "Attachment"; break; case ImageLayout::eRenderingLocalRead: stream << "RenderingLocalRead"; break; case ImageLayout::ePresentSrc: stream << "PresentSrc"; break; case ImageLayout::eVideoDecodeDst: stream << "VideoDecodeDst"; break; case ImageLayout::eVideoDecodeSrc: stream << "VideoDecodeSrc"; break; case ImageLayout::eVideoDecodeDpb: stream << "VideoDecodeDpb"; break; case ImageLayout::eSharedPresent: stream << "SharedPresent"; break; case ImageLayout::eFragmentDensityMap: stream << "FragmentDensityMap"; break; case ImageLayout::eFragmentShadingRateAttachment: stream << "FragmentShadingRateAttachment"; break; case ImageLayout::eVideoEncodeDst: stream << "VideoEncodeDst"; break; case ImageLayout::eVideoEncodeSrc: stream << "VideoEncodeSrc"; break; case ImageLayout::eVideoEncodeDpb: stream << "VideoEncodeDpb"; break; case ImageLayout::eAttachmentFeedbackLoop: stream << "AttachmentFeedbackLoop"; break; case ImageLayout::eVideoEncodeQuantizationMap: stream << "VideoEncodeQuantizationMap"; break; } return stream; } std::ostream & operator<<( std::ostream & stream, AttachmentLoadOp const & v ) { switch ( v ) { case AttachmentLoadOp::eLoad: stream << "Load"; break; case AttachmentLoadOp::eClear: stream << "Clear"; break; case AttachmentLoadOp::eDontCare: stream << "DontCare"; break; case AttachmentLoadOp::eNone: stream << "None"; break; } return stream; } std::ostream & operator<<( std::ostream & stream, AttachmentStoreOp const & v ) { switch ( v ) { case AttachmentStoreOp::eStore: stream << "eStore"; break; case AttachmentStoreOp::eDontCare: stream << "DontCare"; break; case AttachmentStoreOp::eNone: stream << "None"; break; } return stream; } } ================================================ FILE: test/Common.hpp ================================================ #pragma once #include #include #include "BaseTest.hpp" #include namespace test { crg::BufferData createBuffer( std::string name ); crg::BufferViewData createView( std::string name , crg::BufferId buffer , crg::PixelFormat format = crg::PixelFormat::eUNDEFINED ); crg::BufferViewData createView( std::string name , crg::BufferId buffer , crg::DeviceSize offset, crg::DeviceSize size , crg::PixelFormat format = crg::PixelFormat::eUNDEFINED ); crg::ImageData createImage( std::string name , crg::PixelFormat format , uint32_t mipLevels = 1u , uint32_t arrayLayers = 1u ); crg::ImageData createImage1D( std::string name , crg::PixelFormat format , uint32_t mipLevels = 1u , uint32_t arrayLayers = 1u ); crg::ImageData createImage3D( std::string name , crg::PixelFormat format , uint32_t mipLevels = 1u , uint32_t arrayLayers = 1u ); crg::ImageData createImageCube( std::string name , crg::PixelFormat format , uint32_t mipLevels = 1u , uint32_t arrayLayers = 1u ); crg::ImageViewData createView( std::string name , crg::ImageId image , uint32_t baseMipLevel = 0u , uint32_t levelCount = 1u , uint32_t baseArrayLayer = 0u , uint32_t layerCount = 1u ); crg::ImageViewData createView( std::string name , crg::ImageId image , crg::PixelFormat format , uint32_t baseMipLevel = 0u , uint32_t levelCount = 1u , uint32_t baseArrayLayer = 0u , uint32_t layerCount = 1u ); crg::GraphContext & getDummyContext(); std::string checkRunnable( TestCounts const & testCounts , crg::RunnableGraph * runnable ); inline std::string checkRunnable( TestCounts const & testCounts , crg::RunnableGraphPtr const & runnable ) { return checkRunnable( testCounts, runnable.get() ); } void display( TestCounts const & testCounts , std::ostream & stream , crg::RunnableGraph const & value , bool withColours = {} , bool withIds = {} , bool withGroups = {} ); void display( TestCounts const & testCounts , crg::RunnableGraph const & value , bool withColours = {} , bool withIds = {} , bool withGroups = {} ); template< typename TypeT > crg::Id< TypeT > makeId( [[maybe_unused]] TypeT const & data ) { return { 0u, nullptr }; } using CheckViews = std::function< void( test::TestCounts & , crg::FramePass const & , crg::RunnableGraph const & , crg::RecordContext & , uint32_t ) >; crg::RunnablePassPtr createDummy( test::TestCounts & testCounts , crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , CheckViews checkViews , uint32_t index , bool enabled , crg::ru::Config config = {} ); crg::RunnablePassPtr createDummy( test::TestCounts & testCounts , crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , CheckViews checkViews , uint32_t index , crg::ru::Config config = {} ); crg::RunnablePassPtr createDummy( test::TestCounts & testCounts , crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , CheckViews checkViews , crg::ru::Config config = {} ); crg::RunnablePassPtr createDummy( test::TestCounts & testCounts , crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , crg::ru::Config config = {} ); crg::RunnablePassPtr createDummyNoRecord( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph , crg::PipelineStageFlags pipelineStageFlags , crg::ru::Config config = {} ); void checkDummy( test::TestCounts const & testCounts , crg::FramePass const & framePass , crg::RunnableGraph const & graph , crg::RecordContext const & context , uint32_t index ); } namespace crg { std::ostream & operator<<( std::ostream & stream, AccessFlags const & v ); std::ostream & operator<<( std::ostream & stream, ImageLayout const & v ); std::ostream & operator<<( std::ostream & stream, AttachmentLoadOp const & v ); std::ostream & operator<<( std::ostream & stream, AttachmentStoreOp const & v ); } ================================================ FILE: test/TestAttachment.cpp ================================================ #include "Common.hpp" #include #include #include #include namespace { constexpr crg::SamplerDesc defaultSamplerDesc{}; enum class Binding { eUniform, eSampled, eStorage, }; TEST( Attachment, SampledAttachment ) { testBegin( "testSampledAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto tmpAttach1 = crg::Attachment::createDefault( view ); auto tmpAttach2 = crg::Attachment::createDefault( view ); tmpAttach2 = std::move( tmpAttach1 ); tmpAttach1 = tmpAttach2; auto viewAttach = std::move( tmpAttach2 ); pass.addInputSampled( viewAttach, 1u ); require( pass.getSampled().size() == 1u ) auto attachIt = pass.getSampled().begin(); auto const & attachment = attachIt->second; check( attachment.attach->isImage() ) check( attachment.attach->isInput() ) check( !attachment.attach->isOutput() ) check( !attachment.attach->isClearable() ) check( !attachment.attach->isTransitionImageView() ) check( !attachment.attach->isTransferImageView() ) check( !attachment.attach->imageAttach.isDepthStencilTarget() ) check( !attachment.attach->imageAttach.isDepthTarget() ) check( !attachment.attach->imageAttach.isStencilTarget() ) check( !attachment.attach->imageAttach.isStencilInputTarget() ) check( !attachment.attach->imageAttach.isStencilOutputTarget() ) check( attachment.attach->name == pass.getGroupName() + "/" + view.data->name + "/Spl" ) checkEqual( attachment.attach->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment.attach->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment.attach->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment.attach->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment.attach->view() == view ) check( attachIt->first == 1u ) check( attachment.sampler == defaultSamplerDesc ) checkEqual( attachment.attach->getImageLayout( false ), crg::ImageLayout::eShaderReadOnly ) checkEqual( attachment.attach->getImageLayout( true ), crg::ImageLayout::eShaderReadOnly ) testEnd() } TEST( Attachment, SampledAttachmentT ) { testBegin( "testSampledAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto tmpAttach1 = crg::Attachment::createDefault( view ); auto tmpAttach2 = crg::Attachment::createDefault( view ); tmpAttach2 = std::move( tmpAttach1 ); tmpAttach1 = tmpAttach2; auto viewAttach = std::move( tmpAttach2 ); pass.addInputSampledT( viewAttach, Binding::eSampled ); require( pass.getSampled().size() == 1u ) auto attachIt = pass.getSampled().begin(); auto const & attachment = attachIt->second; check( attachment.attach->isImage() ) check( attachment.attach->isInput() ) check( !attachment.attach->isOutput() ) check( !attachment.attach->isClearable() ) check( !attachment.attach->isTransitionImageView() ) check( !attachment.attach->isTransferImageView() ) check( !attachment.attach->imageAttach.isDepthStencilTarget() ) check( !attachment.attach->imageAttach.isDepthTarget() ) check( !attachment.attach->imageAttach.isStencilTarget() ) check( !attachment.attach->imageAttach.isStencilInputTarget() ) check( !attachment.attach->imageAttach.isStencilOutputTarget() ) check( attachment.attach->name == pass.getGroupName() + "/" + view.data->name + "/Spl" ) checkEqual( attachment.attach->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment.attach->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment.attach->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment.attach->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment.attach->view() == view ) check( attachIt->first == uint32_t( Binding::eSampled ) ) check( attachment.sampler == defaultSamplerDesc ) checkEqual( attachment.attach->getImageLayout( false ), crg::ImageLayout::eShaderReadOnly ) checkEqual( attachment.attach->getImageLayout( true ), crg::ImageLayout::eShaderReadOnly ) testEnd() } TEST( Attachment, SampledImage ) { testBegin( "testSampledImage" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputSampledImage( view, 1u ); require( pass.getSampled().size() == 1u ) auto attachIt = pass.getSampled().begin(); auto const & attachment = attachIt->second; check( attachment.attach->isImage() ) check( attachment.attach->isInput() ) check( !attachment.attach->isOutput() ) check( !attachment.attach->isClearable() ) check( !attachment.attach->isTransitionImageView() ) check( !attachment.attach->isTransferImageView() ) check( !attachment.attach->imageAttach.isDepthStencilTarget() ) check( !attachment.attach->imageAttach.isDepthTarget() ) check( !attachment.attach->imageAttach.isStencilTarget() ) check( !attachment.attach->imageAttach.isStencilInputTarget() ) check( !attachment.attach->imageAttach.isStencilOutputTarget() ) check( attachment.attach->name == pass.getGroupName() + "/" + view.data->name + "/Spl" ) checkEqual( attachment.attach->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment.attach->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment.attach->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment.attach->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment.attach->view() == view ) check( attachIt->first == 1u ) check( attachment.sampler == defaultSamplerDesc ) checkEqual( attachment.attach->getImageLayout( false ), crg::ImageLayout::eShaderReadOnly ) checkEqual( attachment.attach->getImageLayout( true ), crg::ImageLayout::eShaderReadOnly ) testEnd() } TEST( Attachment, SampledImageT ) { testBegin( "testSampledImageT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputSampledImageT( view, Binding::eSampled ); require( pass.getSampled().size() == 1u ) auto attachIt = pass.getSampled().begin(); auto const & attachment = attachIt->second; check( attachment.attach->isImage() ) check( attachment.attach->isInput() ) check( !attachment.attach->isOutput() ) check( !attachment.attach->isClearable() ) check( !attachment.attach->isTransitionImageView() ) check( !attachment.attach->isTransferImageView() ) check( !attachment.attach->imageAttach.isDepthStencilTarget() ) check( !attachment.attach->imageAttach.isDepthTarget() ) check( !attachment.attach->imageAttach.isStencilTarget() ) check( !attachment.attach->imageAttach.isStencilInputTarget() ) check( !attachment.attach->imageAttach.isStencilOutputTarget() ) check( attachment.attach->name == pass.getGroupName() + "/" + view.data->name + "/Spl" ) checkEqual( attachment.attach->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment.attach->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment.attach->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment.attach->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment.attach->view() == view ) check( attachIt->first == uint32_t( Binding::eSampled ) ) check( attachment.sampler == defaultSamplerDesc ) checkEqual( attachment.attach->getImageLayout( false ), crg::ImageLayout::eShaderReadOnly ) checkEqual( attachment.attach->getImageLayout( true ), crg::ImageLayout::eShaderReadOnly ) testEnd() } TEST( Attachment, ImplicitColourAttachment ) { testBegin( "testImplicitColourAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( crg::ImageData{ "Test", crg::ImageCreateFlags::eNone, crg::ImageType::e3D, crg::PixelFormat::eR32G32B32A32_SFLOAT, { 1024, 1024, 1024 }, crg::ImageUsageFlags::eSampled, 1u, 1u } ); auto range = crg::getVirtualRange( image, crg::ImageViewType::e3D, { crg::ImageAspectFlags::eColor, 0u, 1u, 0u, 1u } ); check( range.baseArrayLayer == 0 ) auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); auto imageLayout = crg::ImageLayout::eShaderReadOnly; pass.addImplicit( viewAttach, imageLayout ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/Impl" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), imageLayout ) checkEqual( attachment->getImageLayout( true ), imageLayout ) testEnd() } TEST( Attachment, ImplicitDepthAttachment ) { testBegin( "testImplicitDepthAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); auto imageLayout = crg::ImageLayout::ePreinitialized; pass.addImplicit( viewAttach, imageLayout ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/Impl" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), imageLayout ) checkEqual( attachment->getImageLayout( true ), imageLayout ) testEnd() } TEST( Attachment, ImplicitDepthStencilAttachment ) { testBegin( "testImplicitDepthStencilAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); auto imageLayout = crg::ImageLayout::eShaderReadOnly; pass.addImplicit( viewAttach, imageLayout ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/Impl" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), imageLayout ) checkEqual( attachment->getImageLayout( true ), imageLayout ) testEnd() } TEST( Attachment, InStorageAttachment ) { testBegin( "testInStorageAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInputStorage( viewAttach, 1u ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == 1u ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, InStorageAttachmentT ) { testBegin( "testInStorageAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInputStorageT( viewAttach, Binding::eStorage ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, InStorageImage ) { testBegin( "testInStorageImage" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputStorageImage( view, 1u ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == 1u ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, InStorageImageT ) { testBegin( "testInStorageImageT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputStorageImageT( view, Binding::eStorage ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, InStorageBuffer ) { testBegin( "testInStorageBuffer" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto view = graph.createView( test::createView( "Test", buffer ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputStorageBuffer( view, 1u ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( attachment->isStorageBuffer() ) check( attachment->bufferAttach.isStorage() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/SB" ) check( attachment->buffer() == view ) check( attachIt->first == 1u ) testEnd() } TEST( Attachment, InStorageBufferT ) { testBegin( "testInStorageBufferT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto view = graph.createView( test::createView( "Test", buffer ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputStorageBufferT( view, Binding::eStorage ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( attachment->isStorageBuffer() ) check( attachment->bufferAttach.isStorage() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/SB" ) check( attachment->buffer() == view ) check( attachIt->first == uint32_t( Binding::eStorage ) ) testEnd() } TEST( Attachment, UniformAttachment ) { testBegin( "testUniformAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto view = graph.createView( test::createView( "Test", buffer ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); check( &pass.getGraph() == &graph ) auto tmpAttach1 = crg::Attachment::createDefault( view ); auto tmpAttach2 = crg::Attachment::createDefault( view ); tmpAttach2 = std::move( tmpAttach1 ); tmpAttach1 = tmpAttach2; auto viewAttach = std::move( tmpAttach2 ); pass.addInputUniform( viewAttach, 1u ); require( pass.getUniforms().size() == 1u ) auto attachIt = pass.getUniforms().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->isStorageBuffer() ) check( attachment->isUniformBuffer() ) check( !attachment->bufferAttach.isStorage() ) check( attachment->bufferAttach.isUniform() ) check( attachment->name == pass.getGroupName() + "/" + view.data->name + "/UB" ) check( attachIt->first == 1u ) testEnd() } TEST( Attachment, UniformAttachmentT ) { testBegin( "testUniformAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto view = graph.createView( test::createView( "Test", buffer ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto tmpAttach1 = crg::Attachment::createDefault( view ); auto tmpAttach2 = crg::Attachment::createDefault( view ); tmpAttach2 = std::move( tmpAttach1 ); tmpAttach1 = tmpAttach2; auto viewAttach = std::move( tmpAttach2 ); pass.addInputUniformT( viewAttach, Binding::eUniform ); require( pass.getUniforms().size() == 1u ) auto attachIt = pass.getUniforms().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->isStorageBuffer() ) check( attachment->isUniformBuffer() ) check( !attachment->bufferAttach.isStorage() ) check( attachment->bufferAttach.isUniform() ) check( attachment->name == pass.getGroupName() + "/" + view.data->name + "/UB" ) check( attachIt->first == uint32_t( Binding::eUniform ) ) testEnd() } TEST( Attachment, OutStorageAttachment ) { testBegin( "testOutStorageAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addOutputStorageImage( view, 1u ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/OStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == 1u ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, OutStorageAttachmentT ) { testBegin( "testOutStorageAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addOutputStorageImageT( view, Binding::eStorage ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/OStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, ClearOutStorageAttachment ) { testBegin( "testClearOutStorageAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addClearableOutputStorageImage( view, 1u ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/COStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == 1u ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, ClearOutStorageAttachmentT ) { testBegin( "testClearOutStorageAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addClearableOutputStorageImageT( view, Binding::eStorage ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/COStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, InOutStorageAttachment ) { testBegin( "testInOutStorageAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInOutStorage( viewAttach, 1u ); require( pass.getInouts().size() == 1u ) auto attachIt = pass.getInouts().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IOStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == 1u ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, InOutStorageAttachmentT ) { testBegin( "testInOutStorageAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInOutStorageT( viewAttach, Binding::eStorage ); require( pass.getInouts().size() == 1u ) auto attachIt = pass.getInouts().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IOStr" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eGeneral ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eGeneral ) testEnd() } TEST( Attachment, InTransferAttachment ) { testBegin( "testInTransferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInputTransfer( viewAttach ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/ITrf" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eTransferSrc ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eTransferSrc ) testEnd() } TEST( Attachment, InTransferImage ) { testBegin( "testInTransferImage" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputTransferImage( view ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/ITrf" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eTransferSrc ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eTransferSrc ) testEnd() } TEST( Attachment, InTransferBuffer ) { testBegin( "testInTransferBuffer" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto view = graph.createView( test::createView( "Test", buffer ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputTransferBuffer( view ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( attachment->isTransferBuffer() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/ITrf" ) check( attachment->buffer() == view ) testEnd() } TEST( Attachment, OutTransferAttachment ) { testBegin( "testOutTransferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addOutputTransferImage( view ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/OT" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eTransferDst ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eTransferDst ) testEnd() } TEST( Attachment, InOutTransferAttachment ) { testBegin( "testInOutTransferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInOutTransfer( viewAttach ); require( pass.getInouts().size() == 1u ) auto attachIt = pass.getInouts().begin(); auto const & attachment = attachIt->second; check( attachment->isImage() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isTransitionImageView() ) check( attachment->isTransferImageView() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IOTrf" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eTransferDst ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eTransferDst ) testEnd() } TEST( Attachment, InColourAttachment ) { testBegin( "testInColourAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInputColourTarget( viewAttach ); require( pass.getTargets().size() == 1u ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IRcl" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eColorAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eColorAttachment ) testEnd() } TEST( Attachment, InColourImage ) { testBegin( "testInColourImage" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputColourTargetImage( view ); require( pass.getTargets().size() == 1u ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IRcl" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eColorAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eColorAttachment ) testEnd() } TEST( Attachment, OutColourAttachment ) { testBegin( "testOutColourAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addOutputColourTarget( view ); require( pass.getTargets().size() == 1u ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/ORcl" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eClear ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eStore ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eColorAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eColorAttachment ) testEnd() } TEST( Attachment, InOutColourAttachment ) { testBegin( "testInOutColourAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInOutColourTarget( viewAttach ); require( pass.getTargets().size() == 1u ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( attachment->isColourImageTarget() ) check( attachment->isColourInOutImageTarget() ) check( attachment->isColourInputImageTarget() ) check( attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IORcl" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eStore ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eColorAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eColorAttachment ) testEnd() } TEST( Attachment, InDepthAttachment ) { testBegin( "testInDepthAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInputDepthTarget( viewAttach ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IRdp" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilReadOnly ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eDepthReadOnly ) testEnd() } TEST( Attachment, InDepthImage ) { testBegin( "testInDepthImage" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputDepthTargetImage( view ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IRdp" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilReadOnly ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eDepthReadOnly ) testEnd() } TEST( Attachment, OutDepthAttachment ) { testBegin( "testOutDepthAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addOutputDepthTarget( view ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/ORdp" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eClear ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eStore ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eDepthAttachment ) testEnd() } TEST( Attachment, InOutDepthAttachment ) { testBegin( "testInOutDepthAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInOutDepthTarget( viewAttach ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( attachment->imageAttach.isDepthTarget() ) check( !attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IORdp" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eStore ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eDepthAttachment ) check( attachment->view() == view ) testEnd() } TEST( Attachment, InDepthStencilAttachment ) { testBegin( "testInDepthStencilAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInputDepthStencilTarget( viewAttach ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( attachment->imageAttach.isDepthStencilTarget() ) check( attachment->imageAttach.isDepthTarget() ) check( attachment->imageAttach.isStencilTarget() ) check( attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IRds" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilReadOnly ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eDepthStencilReadOnly ) testEnd() } TEST( Attachment, InDepthStencilImage ) { testBegin( "testInDepthStencilImage" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputDepthStencilTargetImage( view ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( attachment->imageAttach.isDepthStencilTarget() ) check( attachment->imageAttach.isDepthTarget() ) check( attachment->imageAttach.isStencilTarget() ) check( attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IRds" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilReadOnly ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eDepthStencilReadOnly ) testEnd() } TEST( Attachment, OutDepthStencilAttachment ) { testBegin( "testOutDepthStencilAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addOutputDepthStencilTarget( view ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( attachment->imageAttach.isDepthStencilTarget() ) check( attachment->imageAttach.isDepthTarget() ) check( attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/ORds" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eClear ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eStore ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eClear ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eStore ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eDepthStencilAttachment ) testEnd() } TEST( Attachment, InOutDepthStencilAttachment ) { testBegin( "testInOutDepthStencilAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInOutDepthStencilTarget( viewAttach ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( attachment->imageAttach.isDepthStencilTarget() ) check( attachment->imageAttach.isDepthTarget() ) check( attachment->imageAttach.isStencilTarget() ) check( attachment->imageAttach.isStencilInputTarget() ) check( attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IORds" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eStore ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eStore ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eDepthStencilAttachment ) testEnd() } TEST( Attachment, InStencilAttachment ) { testBegin( "testInStencilAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eS8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInputStencilTarget( viewAttach ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( attachment->imageAttach.isStencilTarget() ) check( attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IRst" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilReadOnly ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eStencilReadOnly ) testEnd() } TEST( Attachment, InStencilImage ) { testBegin( "testInStencilImage" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eS8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addInputStencilTargetImage( view ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( attachment->imageAttach.isStencilTarget() ) check( attachment->imageAttach.isStencilInputTarget() ) check( !attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IRst" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eDontCare ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilReadOnly ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eStencilReadOnly ) testEnd() } TEST( Attachment, OutStencilAttachment ) { testBegin( "testOutStencilAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eS8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); pass.addOutputStencilTarget( view ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( attachment->imageAttach.isStencilTarget() ) check( !attachment->imageAttach.isStencilInputTarget() ) check( attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/ORst" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eClear ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eStore ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eStencilAttachment ) testEnd() } TEST( Attachment, InOutStencilAttachment ) { testBegin( "testInOutStencilAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eS8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto viewAttach = crg::Attachment::createDefault( view ); pass.addInOutStencilTarget( viewAttach ); require( !pass.getTargets().empty() ) auto const & attachment = pass.getTargets()[0]; check( attachment->isImage() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isClearable() ) check( !attachment->isColourImageTarget() ) check( !attachment->isColourInOutImageTarget() ) check( !attachment->isColourInputImageTarget() ) check( !attachment->isColourOutputImageTarget() ) check( !attachment->isTransitionImageView() ) check( !attachment->isTransferImageView() ) check( !attachment->imageAttach.isColourTarget() ) check( !attachment->imageAttach.isDepthStencilTarget() ) check( !attachment->imageAttach.isDepthTarget() ) check( attachment->imageAttach.isStencilTarget() ) check( attachment->imageAttach.isStencilInputTarget() ) check( attachment->imageAttach.isStencilOutputTarget() ) checkEqual( attachment->name, pass.getGroupName() + "/" + view.data->name + "/IORst" ) checkEqual( attachment->getLoadOp(), crg::AttachmentLoadOp::eDontCare ) checkEqual( attachment->getStoreOp(), crg::AttachmentStoreOp::eDontCare ) checkEqual( attachment->getStencilLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment->getStencilStoreOp(), crg::AttachmentStoreOp::eStore ) check( attachment->view() == view ) checkEqual( attachment->getImageLayout( false ), crg::ImageLayout::eDepthStencilAttachment ) checkEqual( attachment->getImageLayout( true ), crg::ImageLayout::eStencilAttachment ) testEnd() } TEST( Attachment, ImageAttachment ) { testBegin( "testImageAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto image = graph.createImage( test::createImage( "Test", crg::PixelFormat::eS8_UINT ) ); auto view = graph.createView( test::createView( "Test", image ) ); auto const attachment = crg::Attachment::createDefault( view ); check( attachment.isImage() ) check( !attachment.isInput() ) check( !attachment.isOutput() ) check( !attachment.isClearable() ) check( attachment.isColourImageTarget() ) check( !attachment.isColourInOutImageTarget() ) check( !attachment.isColourInputImageTarget() ) check( !attachment.isColourOutputImageTarget() ) check( !attachment.isTransitionImageView() ) check( !attachment.isTransferImageView() ) check( attachment.imageAttach.isColourTarget() ) check( !attachment.imageAttach.isDepthStencilTarget() ) check( !attachment.imageAttach.isDepthTarget() ) check( !attachment.imageAttach.isStencilTarget() ) check( !attachment.imageAttach.isStencilInputTarget() ) check( !attachment.imageAttach.isStencilOutputTarget() ) checkEqual( attachment.getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment.getStoreOp(), crg::AttachmentStoreOp::eStore ) checkEqual( attachment.getStencilLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment.getStencilStoreOp(), crg::AttachmentStoreOp::eStore ) check( attachment.view() == view ) checkEqual( attachment.getImageLayout( false ), crg::ImageLayout::eColorAttachment ) checkEqual( attachment.getImageLayout( true ), crg::ImageLayout::eColorAttachment ) testEnd() } TEST( Attachment, ImplicitBufferAttachment ) { testBegin( "testImplicitBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); crg::AccessState accessState{}; pass.addImplicit( bufferAttach, accessState ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; checkEqual( getSize( buffer ), 1024u ) checkEqual( getSize( bufferv ), 1024u ) check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( !attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/Impl" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) checkEqual( attachment->getAccessMask(), accessState.access ) checkEqual( attachment->getPipelineStageFlags( true ), accessState.pipelineStage ) testEnd() } TEST( Attachment, UniformBufferAttachment ) { testBegin( "testUniformBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); pass.addInputUniformBuffer( bufferv, 1u ); require( pass.getUniforms().size() == 1u ) auto attachIt = pass.getUniforms().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( !attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/UB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, UniformBufferAttachmentT ) { testBegin( "testUniformBufferAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); pass.addInputUniformBufferT( bufferv, Binding::eUniform ); require( pass.getUniforms().size() == 1u ) auto attachIt = pass.getUniforms().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( !attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eUniform ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/UB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InputStorageBufferAttachment ) { testBegin( "testInputStorageBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInputStorage( bufferAttach, 1u ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/IStr" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InputStorageBufferAttachmentT ) { testBegin( "testInputStorageBufferAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInputStorageT( bufferAttach, Binding::eStorage ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/IStr" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, OutputStorageBufferAttachment ) { testBegin( "testOutputStorageBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); pass.addOutputStorageBuffer( bufferv, 1u ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/OSB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, OutputStorageBufferAttachmentT ) { testBegin( "testOutputStorageBufferAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); pass.addOutputStorageBufferT( bufferv, Binding::eStorage ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/OSB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, ClearableOutputStorageBufferAttachment ) { testBegin( "testClearableOutputStorageBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); pass.addClearableOutputStorageBuffer( bufferv, 1u ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/OSB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, ClearableOutputStorageBufferAttachmentT ) { testBegin( "testClearableOutputStorageBufferAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); pass.addClearableOutputStorageBufferT( bufferv, Binding::eStorage ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/OSB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InOutStorageBufferAttachment ) { testBegin( "testInOutStorageBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInOutStorage( bufferAttach, 1u ); require( pass.getInouts().size() == 1u ) auto attachIt = pass.getInouts().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/IOStr" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InOutStorageBufferAttachmentT ) { testBegin( "testInOutStorageBufferAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInOutStorageT( bufferAttach, Binding::eStorage ); require( pass.getInouts().size() == 1u ) auto attachIt = pass.getInouts().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/IOStr" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, ImplicitBufferViewAttachment ) { testBegin( "testImplicitBufferViewAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addImplicit( bufferAttach, crg::AccessState{} ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( !attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( attachment->isTransitionBuffer() ) check( attachment->isTransitionBufferView() ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/Impl" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, UniformBufferViewAttachment ) { testBegin( "testUniformBufferViewAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); pass.addInputUniformBuffer( bufferv, 1u ); require( !pass.getUniforms().empty() ) auto attachIt = pass.getUniforms().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( !attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( attachment->isUniformBuffer() ) check( attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/UB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, UniformBufferViewAttachmentT ) { testBegin( "testUniformBufferViewAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); pass.addInputUniformBufferT( bufferv, Binding::eUniform ); require( !pass.getUniforms().empty() ) auto attachIt = pass.getUniforms().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( !attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( attachment->isUniformBuffer() ) check( attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eUniform ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/UB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InputStorageBufferViewAttachment ) { testBegin( "testInputStorageBufferViewAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInputStorage( bufferAttach, 1u ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/IStr" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InputStorageBufferViewAttachmentT ) { testBegin( "testInputStorageBufferViewAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInputStorageT( bufferAttach, Binding::eStorage ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/IStr" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, OutputStorageBufferViewAttachment ) { testBegin( "testOutputStorageBufferViewAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); pass.addOutputStorageBuffer( bufferv, 1u ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/OSB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, OutputStorageBufferViewAttachmentT ) { testBegin( "testOutputStorageBufferViewAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); pass.addOutputStorageBufferT( bufferv, Binding::eStorage ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/OSB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, ClearableOutputStorageBufferViewAttachment ) { testBegin( "testClearableOutputStorageBufferViewAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); pass.addClearableOutputStorageBuffer( bufferv, 1u ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/OSB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, ClearableOutputStorageBufferViewAttachmentT ) { testBegin( "testClearableOutputStorageBufferViewAttachmentT" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); pass.addClearableOutputStorageBufferT( bufferv, Binding::eStorage ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/OSB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InOutStorageBufferViewAttachment ) { testBegin( "testInOutStorageBufferViewAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInOutStorage( bufferAttach, 1u ); require( pass.getInouts().size() == 1u ) auto attachIt = pass.getInouts().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == 1u ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/IOStr" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InOutStorageBufferViewAttachmentT ) { testBegin( "testInOutStorageBufferViewAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInOutStorageT( bufferAttach, Binding::eStorage ); require( pass.getInouts().size() == 1u ) auto attachIt = pass.getInouts().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( attachment->isBufferView() ) check( !attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( attachment->isStorageBuffer() ) check( attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) check( attachIt->first == uint32_t( Binding::eStorage ) ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/IOStr" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InputTransferBufferAttachment ) { testBegin( "testInputTransferBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInputTransfer( bufferAttach ); require( pass.getInputs().size() == 1u ) auto attachIt = pass.getInputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( !attachment->isOutput() ) check( !attachment->isBufferView() ) check( attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( !attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/ITrf" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, OutputTransferBufferAttachment ) { testBegin( "testOutputTransferBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); pass.addOutputTransferBuffer( bufferv ); require( pass.getOutputs().size() == 1u ) auto attachIt = pass.getOutputs().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( !attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isBufferView() ) check( attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( !attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/OTB" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, InOutTransferBufferAttachment ) { testBegin( "testInOutTransferBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::FramePass & pass = graph.createPass( "test", crg::RunnablePassCreator{} ); auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); auto bufferAttach = crg::Attachment::createDefault( bufferv ); pass.addInOutTransfer( bufferAttach ); require( pass.getInouts().size() == 1u ) auto attachIt = pass.getInouts().begin(); auto const & attachment = attachIt->second; check( attachment->isBuffer() ) check( attachment->isInput() ) check( attachment->isOutput() ) check( !attachment->isBufferView() ) check( attachment->isTransferBuffer() ) check( !attachment->isClearableBuffer() ) check( !attachment->isStorageBuffer() ) check( !attachment->isStorageBufferView() ) check( !attachment->isUniformBuffer() ) check( !attachment->isUniformBufferView() ) check( !attachment->isTransitionBuffer() ) check( !attachment->isTransitionBufferView() ) checkEqual( attachment->name, pass.getGroupName() + "/" + buffer.data->name + "/IOTrf" ) check( attachment->buffer() == bufferv ) check( attachment->bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, BufferAttachment ) { testBegin( "testBufferAttachment" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "Test" ) ); auto bufferv = graph.createView( test::createView( "Test", buffer ) ); auto attachment = crg::Attachment::createDefault( bufferv ); check( attachment.isBuffer() ) check( !attachment.isInput() ) check( !attachment.isOutput() ) check( !attachment.isBufferView() ) check( !attachment.isTransferBuffer() ) check( !attachment.isClearableBuffer() ) check( !attachment.isStorageBuffer() ) check( !attachment.isStorageBufferView() ) check( !attachment.isUniformBuffer() ) check( !attachment.isUniformBufferView() ) check( !attachment.isTransitionBuffer() ) check( !attachment.isTransitionBufferView() ) check( attachment.buffer() == bufferv ) check( attachment.bufferAttach.buffer() == bufferv ) testEnd() } TEST( Attachment, AttachmentMerge ) { testBegin( "testAttachmentMerge" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "buffer1" ) ); auto buffer1v = graph.createView( test::createView( "buffer1v", buffer, 0u, 512u ) ); auto buffer2v = graph.createView( test::createView( "buffer2v", buffer, 512u, 512u ) ); auto image = graph.createImage( test::createImage( "image1", crg::PixelFormat::eR32G32B32A32_SFLOAT, 2u, 2u ) ); auto image1v = graph.createView( test::createView( "image1v", image, 0u, 1u, 0u, 1u ) ); auto image2v = graph.createView( test::createView( "image2v", image, 1u, 1u, 1u, 1u ) ); { // Empty attachment list check( graph.mergeAttachments( {} ) == nullptr ) } { // Single buffer attachment in list auto attachment = crg::Attachment::createDefault( buffer1v ); checkThrow( attachment.getSource( 1u ), crg::Exception ) check( attachment.getSource( 0u ) == &attachment ) check( graph.mergeAttachments( { &attachment } ) == &attachment ) } { // Single image attachment in list auto attachment = crg::Attachment::createDefault( image1v ); check( graph.mergeAttachments( { &attachment } ) == &attachment ) } { // Mixed attachments in list auto attachment1 = crg::Attachment::createDefault( image1v ); auto attachment2 = crg::Attachment::createDefault( buffer1v ); checkThrow( graph.mergeAttachments( { &attachment1, &attachment2 } ), crg::Exception ) } { // Empty image attachments in list auto attachment1 = crg::Attachment::createDefault( image1v ); attachment1.imageAttach.views.clear(); auto attachment2 = crg::Attachment::createDefault( image2v ); attachment2.imageAttach.views.clear(); checkThrow( graph.mergeAttachments( { &attachment1, &attachment2 } ), crg::Exception ) } { // Empty buffer attachments in list auto attachment1 = crg::Attachment::createDefault( buffer1v ); attachment1.bufferAttach.buffers.clear(); auto attachment2 = crg::Attachment::createDefault( buffer2v ); attachment2.bufferAttach.buffers.clear(); checkThrow( graph.mergeAttachments( { &attachment1, &attachment2 } ), crg::Exception ) } { // Image attachments with different pass count auto attachment1 = crg::Attachment::createDefault( image1v ); auto attachment2 = crg::Attachment::createDefault( image2v ); attachment2.imageAttach.views.clear(); checkThrow( graph.mergeAttachments( { &attachment1, &attachment2 } ), crg::Exception ) } { // Buffer attachments with different pass count auto attachment1 = crg::Attachment::createDefault( buffer1v ); auto attachment2 = crg::Attachment::createDefault( buffer2v ); attachment2.bufferAttach.buffers.clear(); checkThrow( graph.mergeAttachments( { &attachment1, &attachment2 } ), crg::Exception ) } { // Image attachments auto attachment1 = crg::Attachment::createDefault( image1v ); auto attachment2 = crg::Attachment::createDefault( image2v ); checkNoThrow( graph.mergeAttachments( { &attachment1, &attachment2 } ) ) auto & attachment = *graph.mergeAttachments( { &attachment1, &attachment2 } ); check( attachment.isImage() ) check( !attachment.isInput() ) check( !attachment.isOutput() ) check( !attachment.isClearable() ) check( attachment.isColourImageTarget() ) check( !attachment.isColourInOutImageTarget() ) check( !attachment.isColourInputImageTarget() ) check( !attachment.isColourOutputImageTarget() ) check( !attachment.isTransitionImageView() ) check( !attachment.isTransferImageView() ) check( attachment.imageAttach.isColourTarget() ) check( !attachment.imageAttach.isDepthStencilTarget() ) check( !attachment.imageAttach.isDepthTarget() ) check( !attachment.imageAttach.isStencilTarget() ) check( !attachment.imageAttach.isStencilInputTarget() ) check( !attachment.imageAttach.isStencilOutputTarget() ) checkEqual( attachment.getLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment.getStoreOp(), crg::AttachmentStoreOp::eStore ) checkEqual( attachment.getStencilLoadOp(), crg::AttachmentLoadOp::eLoad ) checkEqual( attachment.getStencilStoreOp(), crg::AttachmentStoreOp::eStore ) check( attachment.getImageLayout( false ) == crg::ImageLayout::eColorAttachment ) check( attachment.getImageLayout( true ) == crg::ImageLayout::eColorAttachment ) check( attachment.pass == nullptr ) check( attachment.view().data->source.size() == 2u ) check( attachment.view().data->source[0] == image1v ) check( attachment.view().data->source[1] == image2v ) } { // Buffer attachments auto attachment1 = crg::Attachment::createDefault( buffer1v ); auto attachment2 = crg::Attachment::createDefault( buffer2v ); checkNoThrow( graph.mergeAttachments( { &attachment1, &attachment2 } ) ) auto & attachment = *graph.mergeAttachments( { &attachment1, &attachment2 } ); check( attachment.isBuffer() ) check( !attachment.isInput() ) check( !attachment.isOutput() ) check( !attachment.isBufferView() ) check( !attachment.isTransferBuffer() ) check( !attachment.isClearableBuffer() ) check( !attachment.isStorageBuffer() ) check( !attachment.isStorageBufferView() ) check( !attachment.isUniformBuffer() ) check( !attachment.isUniformBufferView() ) check( !attachment.isTransitionBuffer() ) check( !attachment.isTransitionBufferView() ) check( attachment.pass == nullptr ) check( attachment.buffer().data->source.size() == 2u ) check( attachment.buffer().data->source[0] == buffer1v ) check( attachment.buffer().data->source[1] == buffer2v ) } testEnd() } } testSuiteMain() ================================================ FILE: test/TestBases.cpp ================================================ #include "Common.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace { crg::GraphContext & getContext() { return test::getDummyContext(); } } TEST( Bases, BaseFuncs ) { testBegin( "testBaseFuncs" ) crg::ResourceHandler handler; auto image = handler.createImageId( test::createImage( "image", crg::PixelFormat::eR16G16B16A16_SFLOAT, 8u, 6u ) ); auto view = handler.createViewId( test::createView( "view", image, crg::PixelFormat::eR16G16B16A16_SFLOAT, 4u, 2u, 2u, 3u ) ); getMipExtent( view ); auto type = getImageType( image ); check( getImageType( view ) == type ) check( getImageViewType( view ) == view.data->info.viewType ) check( getImageCreateFlags( view ) == getImageCreateFlags( image ) ) check( getMipLevels( image ) == 8u ) check( getMipLevels( view ) == 2u ) check( getArrayLayers( image ) == 6u ) check( getArrayLayers( view ) == 3u ) check( crg::getAccessMask( crg::ImageLayout::ePresentSrc ) == crg::AccessFlags::eMemoryRead ) check( crg::getAccessMask( crg::ImageLayout::eSharedPresent ) == crg::AccessFlags::eMemoryRead ) check( crg::getAccessMask( crg::ImageLayout::eColorAttachment ) == crg::AccessFlags::eColorAttachmentWrite ) check( crg::getAccessMask( crg::ImageLayout::eDepthStencilAttachment ) == crg::AccessFlags::eDepthStencilAttachmentWrite ) check( crg::getAccessMask( crg::ImageLayout::eDepthStencilReadOnly ) == crg::AccessFlags::eDepthStencilAttachmentRead ) check( crg::getAccessMask( crg::ImageLayout::eShaderReadOnly ) == crg::AccessFlags::eShaderRead ) check( crg::getAccessMask( crg::ImageLayout::eTransferSrc ) == crg::AccessFlags::eTransferRead ) check( crg::getAccessMask( crg::ImageLayout::eTransferDst ) == crg::AccessFlags::eTransferWrite ) check( crg::getAccessMask( crg::ImageLayout::eDepthReadOnlyStencilAttachment ) == ( crg::AccessFlags::eDepthStencilAttachmentRead | crg::AccessFlags::eDepthStencilAttachmentWrite ) ) check( crg::getAccessMask( crg::ImageLayout::eDepthAttachmentStencilReadOnly ) == ( crg::AccessFlags::eDepthStencilAttachmentRead | crg::AccessFlags::eDepthStencilAttachmentWrite ) ) #ifdef VK_NV_shading_rate_image check( crg::getAccessMask( crg::ImageLayout::eFragmentShadingRateAttachment ) == crg::AccessFlags::eFragmentShadingRateAttachmentRead ) check( crg::getPipelineState( crg::PipelineStageFlags::eFragmentShadingRateAttachment ).access == crg::AccessFlags::eFragmentShadingRateAttachmentRead ) check( crg::getStageMask( crg::ImageLayout::eFragmentShadingRateAttachment ) == crg::PipelineStageFlags::eFragmentShadingRateAttachment ) #endif #ifdef VK_EXT_fragment_density_map check( crg::getAccessMask( crg::ImageLayout::eFragmentDensityMap ) == crg::AccessFlags::eFragmentDensityMapRead ) check( crg::getStageMask( crg::ImageLayout::eFragmentDensityMap ) == crg::PipelineStageFlags::eFragmentShader ) #endif check( crg::getPipelineState( crg::PipelineStageFlags::eBottomOfPipe ).access == crg::AccessFlags::eMemoryRead ) check( crg::getPipelineState( crg::PipelineStageFlags::eColorAttachmentOutput ).access == ( crg::AccessFlags::eColorAttachmentWrite | crg::AccessFlags::eColorAttachmentRead ) ) check( crg::getPipelineState( crg::PipelineStageFlags::eLateFragmentTests ).access == ( crg::AccessFlags::eDepthStencilAttachmentWrite | crg::AccessFlags::eDepthStencilAttachmentRead ) ) check( crg::getPipelineState( crg::PipelineStageFlags::eFragmentShader ).access == crg::AccessFlags::eShaderRead ) check( crg::getPipelineState( crg::PipelineStageFlags::eTransfer ).access == ( crg::AccessFlags::eTransferRead | crg::AccessFlags::eTransferWrite ) ) check( crg::getPipelineState( crg::PipelineStageFlags::eComputeShader ).access == crg::AccessFlags::eShaderRead ) check( crg::getStageMask( crg::ImageLayout::eUndefined ) == crg::PipelineStageFlags::eHost ) check( crg::getStageMask( crg::ImageLayout::eGeneral ) == crg::PipelineStageFlags::eBottomOfPipe ) check( crg::getStageMask( crg::ImageLayout::ePresentSrc ) == crg::PipelineStageFlags::eBottomOfPipe ) check( crg::getStageMask( crg::ImageLayout::eSharedPresent ) == crg::PipelineStageFlags::eBottomOfPipe ) check( crg::getStageMask( crg::ImageLayout::eDepthStencilReadOnly ) == crg::PipelineStageFlags::eLateFragmentTests ) check( crg::getStageMask( crg::ImageLayout::eDepthReadOnlyStencilAttachment ) == crg::PipelineStageFlags::eLateFragmentTests ) check( crg::getStageMask( crg::ImageLayout::eDepthAttachmentStencilReadOnly ) == crg::PipelineStageFlags::eLateFragmentTests ) check( crg::getStageMask( crg::ImageLayout::eDepthStencilAttachment ) == crg::PipelineStageFlags::eLateFragmentTests ) check( crg::getStageMask( crg::ImageLayout::eColorAttachment ) == crg::PipelineStageFlags::eColorAttachmentOutput ) check( crg::getStageMask( crg::ImageLayout::eShaderReadOnly ) == crg::PipelineStageFlags::eFragmentShader ) check( crg::getStageMask( crg::ImageLayout::eTransferSrc ) == crg::PipelineStageFlags::eTransfer ) check( crg::getStageMask( crg::ImageLayout::eTransferDst ) == crg::PipelineStageFlags::eTransfer ) for ( uint32_t i = 0; i <= uint32_t( crg::PixelFormat::eASTC_12x12_SRGB_BLOCK ); ++i ) checkNoThrow( getName( crg::PixelFormat( i ) ) ) for ( uint32_t i = 0; i <= uint32_t( crg::FilterMode::eLinear ); ++i ) checkNoThrow( getName( crg::FilterMode( i ) ) ) for ( uint32_t i = 0; i <= uint32_t( crg::MipmapMode::eLinear ); ++i ) checkNoThrow( getName( crg::MipmapMode( i ) ) ) for ( uint32_t i = 0; i <= uint32_t( crg::WrapMode::eMirrorClampToEdge ); ++i ) checkNoThrow( getName( crg::WrapMode( i ) ) ) auto vb1 = crg::VertexBuffer{ handler.createViewId( test::createView( "vtx1", handler.createBufferId( test::createBuffer( "vtx1" ) ) ) ) }; auto vb2 = crg::VertexBuffer{ handler.createViewId( test::createView( "vtx2", handler.createBufferId( test::createBuffer( "vtx2" ) ) ) ) }; vb2 = std::move( vb1 ); vb1 = vb2; crg::VertexBuffer vb3{ vb1 }; crg::VertexBuffer vb4{ std::move( vb1 ) }; crg::GetPrimitiveCountCallback cb0; crg::GetPrimitiveCountCallback cb1; cb1 = cb0; testEnd() } TEST( Bases, ClearValues ) { testBegin( "testClearValues" ) crg::ClearColorValue clearColorInt32{ std::array< int32_t, 4u >{ 1, 2, 3, 4 } }; crg::ClearColorValue clearColorUInt32{ std::array< uint32_t, 4u >{ 1u, 2u, 3u, 4u } }; crg::ClearColorValue clearColorFloat32{ std::array< float, 4u >{ 1.0f, 2.0f, 3.0f, 4.0f } }; crg::ClearDepthStencilValue clearDepthStencil{ 1.0f, 255u }; checkNoThrow( getClearDepthStencilValue( crg::ClearValue{ clearColorFloat32 } ) ) checkNoThrow( getClearColorValue( crg::ClearValue{ clearDepthStencil } ) ) checkNoThrow( getClearColorValue( crg::ClearValue{ clearColorFloat32 } ) ) checkNoThrow( getClearDepthStencilValue( crg::ClearValue{ clearDepthStencil } ) ) checkNoThrow( convert( crg::ClearValue{ clearColorFloat32 } ) ) checkNoThrow( convert( crg::ClearValue{ clearDepthStencil } ) ) check( clearColorFloat32.isFloat32() ) check( !clearColorFloat32.isInt32() ) check( !clearColorFloat32.isUInt32() ) check( !clearColorUInt32.isFloat32() ) check( !clearColorUInt32.isInt32() ) check( clearColorUInt32.isUInt32() ) check( !clearColorInt32.isFloat32() ) check( clearColorInt32.isInt32() ) check( !clearColorInt32.isUInt32() ) check( crg::ClearValue{ clearColorFloat32 }.isColor() ) check( !crg::ClearValue{ clearColorFloat32 }.isDepthStencil() ) check( !crg::ClearValue{ clearDepthStencil }.isColor() ) check( crg::ClearValue{ clearDepthStencil }.isDepthStencil() ) { auto vkClearColorFloat32 = crg::convert( clearColorFloat32 ); for ( uint32_t i = 0; i < 4u; ++i ) check( vkClearColorFloat32.float32[i] == clearColorFloat32.float32()[i] ) } { auto vkClearColorFloat32 = crg::convert( crg::ClearValue{ clearColorFloat32 } ); for ( uint32_t i = 0; i < 4u; ++i ) check( vkClearColorFloat32.color.float32[i] == clearColorFloat32.float32()[i] ) } { auto vkClearColorInt32 = crg::convert( clearColorInt32 ); for ( uint32_t i = 0; i < 4u; ++i ) check( vkClearColorInt32.int32[i] == clearColorInt32.int32()[i] ) } { auto vkClearColorInt32 = crg::convert( crg::ClearValue{ clearColorInt32 } ); for ( uint32_t i = 0; i < 4u; ++i ) check( vkClearColorInt32.color.int32[i] == clearColorInt32.int32()[i] ) } { auto vkClearColorUInt32 = crg::convert( clearColorUInt32 ); for ( uint32_t i = 0; i < 4u; ++i ) check( vkClearColorUInt32.uint32[i] == clearColorUInt32.uint32()[i] ) } { auto vkClearColorUInt32 = crg::convert( crg::ClearValue{ clearColorUInt32 } ); for ( uint32_t i = 0; i < 4u; ++i ) check( vkClearColorUInt32.color.uint32[i] == clearColorUInt32.uint32()[i] ) } { auto vkClearDepthStencil = crg::convert( clearDepthStencil ); check( vkClearDepthStencil.depth == clearDepthStencil.depth ) check( vkClearDepthStencil.stencil == clearDepthStencil.stencil ) } { auto vkClearDepthStencil = crg::convert( crg::ClearValue{ clearDepthStencil } ); check( vkClearDepthStencil.depthStencil.depth == clearDepthStencil.depth ) check( vkClearDepthStencil.depthStencil.stencil == clearDepthStencil.stencil ) } testEnd() } TEST( Bases, Signal ) { testBegin( "testSignal" ) { using DummyFunc = std::function< void() >; using OnDummy = crg::Signal< DummyFunc >; OnDummy onDummy; auto connection = onDummy.connect( []() { // Nothing to do here... } ); onDummy(); connection.disconnect(); onDummy(); connection = onDummy.connect( []() { // Nothing to do here... } ); onDummy(); connection = onDummy.connect( []() { // Nothing to do here... } ); onDummy(); auto connection2 = onDummy.connect( []() { // Nothing to do here... } ); onDummy(); } { using DummyFunc = std::function< void() >; using OnDummy = crg::Signal< DummyFunc >; auto onDummy = std::make_unique< OnDummy >(); auto connection = onDummy->connect( []() { // Nothing to do here... } ); ( *onDummy )(); auto connection2 = onDummy->connect( []() { // Nothing to do here... } ); ( *onDummy )(); onDummy.reset(); } { using DummyFunc = std::function< void( bool ) >; using OnDummy = crg::Signal< DummyFunc >; using OnDummyConnection = crg::SignalConnection< OnDummy >; auto onDummy = std::make_unique< OnDummy >(); OnDummyConnection tmpConn; auto connection = onDummy->connect( []( bool ) { // Nothing to do here... } ); ( *onDummy )( false ); auto connection2 = onDummy->connect( [&tmpConn, &onDummy]( bool ) { tmpConn = onDummy->connect( []( bool ) { // Nothing to do here... } ); } ); ( *onDummy )( true ); onDummy.reset(); } testEnd() } TEST( Bases, Exception ) { testBegin( "testException" ) try { CRG_Exception( "Coin !!" ); } catch ( crg::Exception & exc ) { crg::Logger::logInfo( exc.what() ); } testEnd() } TEST( Bases, Fence ) { testBegin( "testFence" ) auto & context = getContext(); { crg::Fence fence{ context, "test", {} }; fence.reset(); fence.wait( 0xFFFFFFFFFFFFFFFFULL ); fence.reset(); } testEnd() } TEST( Bases, FramePassTimer ) { testBegin( "testFramePassTimer" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::RunnablePass * runPass{}; auto buffer = graph.createBuffer( test::createBuffer( "buffer" ) ); auto bufferv = graph.createView( test::createView( "bufferv", buffer ) ); auto & testPass = graph.createPass( "Mesh" , [&testCounts, &runPass]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { auto res = createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); runPass = res.get(); return res; } ); testPass.addClearableOutputStorageBuffer( bufferv, 1u ); crg::FramePassNode node1{ testPass }; crg::FramePassNode node2{ testPass }; node2 = std::move( node1 ); checkThrow( crg::checkVkResult( VK_ERROR_VALIDATION_FAILED_EXT, std::string{ "Test" } ), crg::Exception ) auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); { auto & timer = runPass->getTimer(); auto save = timer.getCpuTime(); { auto block = timer.start(); std::this_thread::sleep_for( std::chrono::milliseconds{ 10u } ); } timer.retrieveGpuTime(); auto end = timer.getCpuTime(); auto total = ( end - save ) + timer.getGpuTime(); check( total >= std::chrono::milliseconds{ 10u } ) timer.reset(); check( timer.getCpuTime() >= std::chrono::milliseconds{ 0u } ) check( timer.getGpuTime() >= std::chrono::milliseconds{ 0u } ) } { crg::FramePassTimer timer{ getContext(), "test", crg::TimerScope::eUpdate }; auto save = timer.getCpuTime(); { auto block = std::make_unique< crg::FramePassTimerBlock >( timer.start() ); std::this_thread::sleep_for( std::chrono::milliseconds{ 10u } ); block.reset(); } timer.retrieveGpuTime(); auto end = timer.getCpuTime(); auto total = ( end - save ) + timer.getGpuTime(); check( total >= std::chrono::milliseconds{ 10u } ) timer.reset(); check( timer.getCpuTime() >= std::chrono::milliseconds{ 0u } ) check( timer.getGpuTime() >= std::chrono::milliseconds{ 0u } ) } { auto timer = std::make_unique< crg::FramePassTimer >( getContext(), "test", crg::TimerScope::eUpdate ); auto connection = timer->onDestroy.connect( []( crg::FramePassTimer const & thisTimer ) { if ( thisTimer.getScope() == crg::TimerScope::eUpdate ) { CRG_Exception( "WTF???" ); } } ); timer.reset(); } testEnd() } TEST( Bases, ImplicitActions ) { testBegin( "testImplicitActions" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto depth1 = graph.createImage( test::createImage( "depth1", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto depth1v = graph.createView( test::createView( "depth1v", depth1, crg::PixelFormat::eD32_SFLOAT_S8_UINT, 0u, 1u, 0u, 1u ) ); auto depth2 = graph.createImage( test::createImage( "depth2", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto depth2v = graph.createView( test::createView( "depth2v", depth2, crg::PixelFormat::eD32_SFLOAT_S8_UINT, 0u, 1u, 0u, 1u ) ); auto colour1 = graph.createImage( test::createImage( "colour1", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour1v = graph.createView( test::createView( "colour1v", colour1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto colour2 = graph.createImage( test::createImage( "colour2", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour2v = graph.createView( test::createView( "colour2v", colour2, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto colour3 = graph.createImage( test::createImage( "colour3", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour3v = graph.createView( test::createView( "colour3v", colour3, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto colour4 = graph.createImage( test::createImage( "colour4", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour4v = graph.createView( test::createView( "colour4v", colour4, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto buffer1 = graph.createBuffer( test::createBuffer( "buffer1" ) ); auto buffer1v = graph.createView( test::createView( "buffer1v", buffer1 ) ); auto buffer2 = graph.createBuffer( test::createBuffer( "buffer2" ) ); auto buffer2v = graph.createView( test::createView( "buffer2v", buffer2 ) ); auto buffer3 = graph.createBuffer( test::createBuffer( "buffer3" ) ); auto buffer3v = graph.createView( test::createView( "buffer3v", buffer3 ) ); auto buffer4 = graph.createBuffer( test::createBuffer( "buffer4" ) ); auto buffer4v = graph.createView( test::createView( "buffer4v", buffer4 ) ); auto & testPass1 = graph.createPass( "Mesh" , [&testCounts, depth2v, colour1v, colour2v, colour3v, colour4v, buffer1v, buffer2v, buffer3v]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { auto depthIt = framePass.getTargets().begin(); auto colourIt = std::next( depthIt ); auto extent3D = getExtent( colour2v ); auto extent2D = crg::Extent2D{ extent3D.width, extent3D.height }; return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , test::checkDummy , 0u , false , crg::ru::Config{} .implicitAction( ( *depthIt )->view(), crg::RecordContext::clearAttachment( **depthIt, crg::ImageLayout::eDepthStencilAttachment ) ) .implicitAction( depth2v, crg::RecordContext::clearAttachment( depth2v, crg::ClearDepthStencilValue{}, crg::ImageLayout::eDepthStencilAttachment ) ) .implicitAction( ( *colourIt )->view(), crg::RecordContext::clearAttachment( **colourIt ) ) .implicitAction( colour4v, crg::RecordContext::clearAttachment( colour4v, crg::ClearColorValue{}, crg::ImageLayout::eShaderReadOnly ) ) .implicitAction( colour2v, crg::RecordContext::blitImage( colour1v, colour2v, { {}, extent2D }, { {}, extent2D }, crg::FilterMode::eLinear, crg::ImageLayout::eShaderReadOnly ) ) .implicitAction( colour3v, crg::RecordContext::copyImage( colour2v, colour3v, extent2D, crg::ImageLayout::eShaderReadOnly ) ) .implicitAction( buffer1v, crg::RecordContext::clearBuffer( buffer1v, { crg::AccessFlags::eShaderWrite, crg::PipelineStageFlags::eFragmentShader } ) ) .implicitAction( buffer2v, crg::RecordContext::clearBuffer( buffer2v, 18u, { crg::AccessFlags::eShaderWrite, crg::PipelineStageFlags::eFragmentShader } ) ) .implicitAction( buffer3v, crg::RecordContext::copyBuffer( buffer1v, buffer3v, 0u, 0u, 48u, { crg::AccessFlags::eShaderWrite, crg::PipelineStageFlags::eFragmentShader } ) ) ); } ); auto depth1a = testPass1.addOutputDepthStencilTarget( depth1v ); auto colour1a = testPass1.addOutputColourTarget( colour1v ); auto colour2a = testPass1.addOutputColourTarget( colour2v ); auto colour3a = testPass1.addOutputColourTarget( colour3v ); auto colour4a = testPass1.addOutputColourTarget( colour4v ); auto depth2a = testPass1.addOutputStorageImage( depth2v, 0u ); auto buffer2a = testPass1.addOutputStorageBuffer( buffer2v, 1 ); auto buffer3a = testPass1.addOutputStorageBuffer( buffer3v, 2 ); auto buffer4a = testPass1.addOutputStorageBuffer( buffer4v, 3 ); auto buffer1a = testPass1.addOutputStorageBuffer( buffer1v, 4 ); auto & testPass2 = graph.createPass( "Pass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , test::checkDummy , 0u ); } ); testPass2.addInOutDepthStencilTarget( *depth1a ); testPass2.addInOutColourTarget( *colour1a ); testPass2.addInOutColourTarget( *colour2a ); testPass2.addInOutColourTarget( *colour3a ); testPass2.addInOutColourTarget( *colour4a ); testPass2.addInOutStorage( *depth2a, 0u ); testPass2.addInOutStorage( *buffer2a, 1 ); testPass2.addInOutStorage( *buffer3a, 2 ); testPass2.addInOutStorage( *buffer4a, 3 ); testPass2.addInOutStorage( *buffer1a, 4 ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( Bases, PrePassActions ) { testBegin( "testPrePassActions" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto depth1 = graph.createImage( test::createImage( "depth1", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto depth1v = graph.createView( test::createView( "depth1v", depth1, crg::PixelFormat::eD32_SFLOAT_S8_UINT, 0u, 1u, 0u, 1u ) ); auto depth2 = graph.createImage( test::createImage( "depth2", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto depth2v = graph.createView( test::createView( "depth2v", depth2, crg::PixelFormat::eD32_SFLOAT_S8_UINT, 0u, 1u, 0u, 1u ) ); auto colour1 = graph.createImage( test::createImage( "colour1", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour1v = graph.createView( test::createView( "colour1v", colour1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto colour2 = graph.createImage( test::createImage( "colour2", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour2v = graph.createView( test::createView( "colour2v", colour2, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto colour3 = graph.createImage( test::createImage( "colour3", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour3v = graph.createView( test::createView( "colour3v", colour3, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto colour4 = graph.createImage( test::createImage( "colour4", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour4v = graph.createView( test::createView( "colour4v", colour4, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto & testPass1 = graph.createPass( "Mesh" , [&testCounts, depth2v, colour1v, colour2v, colour3v, colour4v]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { auto depthIt = framePass.getTargets().begin(); auto colourIt = std::next( depthIt ); auto extent3D = getExtent( colour2v ); auto extent2D = crg::Extent2D{ extent3D.width, extent3D.height }; return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , test::checkDummy , crg::ru::Config{} .prePassAction( crg::RecordContext::clearAttachment( **depthIt, crg::ImageLayout::eDepthStencilAttachment ) ) .prePassAction( crg::RecordContext::clearAttachment( depth2v, crg::ClearDepthStencilValue{}, crg::ImageLayout::eDepthStencilAttachment ) ) .prePassAction( crg::RecordContext::clearAttachment( **colourIt ) ) .prePassAction( crg::RecordContext::clearAttachment( colour4v, crg::ClearColorValue{} ) ) .prePassAction( crg::RecordContext::blitImage( colour1v, colour2v, { {}, extent2D }, { {}, extent2D }, crg::FilterMode::eLinear, crg::ImageLayout::eShaderReadOnly ) ) .prePassAction( crg::RecordContext::copyImage( colour2v, colour3v, extent2D, crg::ImageLayout::eShaderReadOnly ) ) ); } ); auto depth1a = testPass1.addOutputDepthStencilTarget( depth1v ); auto colour1a = testPass1.addOutputColourTarget( colour1v ); auto colour2a = testPass1.addOutputColourTarget( colour2v ); auto colour3a = testPass1.addOutputColourTarget( colour3v ); auto colour4a = testPass1.addOutputColourTarget( colour4v ); auto depth2a = testPass1.addOutputStorageImage( depth2v, 0u ); auto & testPass2 = graph.createPass( "Pass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , test::checkDummy , 0u ); } ); testPass2.addInOutDepthStencilTarget( *depth1a ); testPass2.addInOutColourTarget( *colour1a ); testPass2.addInOutColourTarget( *colour2a ); testPass2.addInOutColourTarget( *colour3a ); testPass2.addInOutColourTarget( *colour4a ); testPass2.addInOutStorage( *depth2a, 0u ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( Bases, PostPassActions ) { testBegin( "testPrePassActions" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto depth1 = graph.createImage( test::createImage( "depth1", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto depth1v = graph.createView( test::createView( "depth1v", depth1, crg::PixelFormat::eD32_SFLOAT_S8_UINT, 0u, 1u, 0u, 1u ) ); auto depth2 = graph.createImage( test::createImage( "depth2", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto depth2v = graph.createView( test::createView( "depth2v", depth2, crg::PixelFormat::eD32_SFLOAT_S8_UINT, 0u, 1u, 0u, 1u ) ); auto colour1 = graph.createImage( test::createImage( "colour1", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour1v = graph.createView( test::createView( "colour1v", colour1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto colour2 = graph.createImage( test::createImage( "colour2", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour2v = graph.createView( test::createView( "colour2v", colour2, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto colour3 = graph.createImage( test::createImage( "colour3", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour3v = graph.createView( test::createView( "colour3v", colour3, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto colour4 = graph.createImage( test::createImage( "colour4", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colour4v = graph.createView( test::createView( "colour4v", colour4, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); crg::RunnablePass const * runPass{}; auto & testPass1 = graph.createPass( "Mesh" , [&runPass , &testCounts, depth2v, colour1v, colour2v, colour3v, colour4v]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { auto depthIt = framePass.getTargets().begin(); auto colourIt = std::next( depthIt ); auto extent3D = getExtent( colour2v ); auto extent2D = crg::Extent2D{ extent3D.width, extent3D.height }; auto res = createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , test::checkDummy , crg::ru::Config{ 1u, true } .postPassAction( crg::RecordContext::clearAttachment( **depthIt, crg::ImageLayout::eDepthStencilAttachment ) ) .postPassAction( crg::RecordContext::clearAttachment( depth2v, crg::ClearDepthStencilValue{}, crg::ImageLayout::eDepthStencilAttachment ) ) .postPassAction( crg::RecordContext::clearAttachment( **colourIt ) ) .postPassAction( crg::RecordContext::clearAttachment( colour4v, crg::ClearColorValue{} ) ) .postPassAction( crg::RecordContext::blitImage( colour1v, colour2v, { {}, extent2D }, { {}, extent2D }, crg::FilterMode::eLinear, crg::ImageLayout::eShaderReadOnly ) ) .postPassAction( crg::RecordContext::copyImage( colour2v, colour3v, extent2D, crg::ImageLayout::eShaderReadOnly ) ) ); runPass = res.get(); return res; } ); auto depth1a = testPass1.addOutputDepthStencilTarget( depth1v ); auto colour1a = testPass1.addOutputColourTarget( colour1v ); auto colour2a = testPass1.addOutputColourTarget( colour2v ); auto colour3a = testPass1.addOutputColourTarget( colour3v ); auto colour4a = testPass1.addOutputColourTarget( colour4v ); auto depth2a = testPass1.addOutputStorageImage( depth2v, 0u ); auto & testPass2 = graph.createPass( "Pass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , test::checkDummy , 0u ); } ); testPass2.addInOutDepthStencilTarget( *depth1a ); testPass2.addInOutColourTarget( *colour1a ); testPass2.addInOutColourTarget( *colour2a ); testPass2.addInOutColourTarget( *colour3a ); testPass2.addInOutColourTarget( *colour4a ); testPass2.addInOutStorage( *depth2a, 0u ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( Bases, GraphDeps ) { testBegin( "testGraphDeps" ) crg::ResourceHandler handler; crg::FrameGraph graph1{ handler, testCounts.testName + "1" }; auto colour = graph1.createImage( test::createImage( "colour", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colourv = graph1.createView( test::createView( "colourv", colour, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto iocolour = graph1.createImage( test::createImage( "iocolour", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto iocolourv = graph1.createView( test::createView( "iocolourv", iocolour, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto buffer = graph1.createBuffer( test::createBuffer( "buffer" ) ); auto bufferv = graph1.createView( test::createView( "bufferv", buffer ) ); auto & testPass1 = graph1.createPass( "Mesh" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto coloura = testPass1.addOutputColourTarget( colourv ); auto iocoloura = testPass1.addOutputColourTarget( iocolourv ); testPass1.addOutputStorageBuffer( bufferv, 0u ); graph1.addOutput( colourv, crg::makeLayoutState( crg::ImageLayout::eShaderReadOnly ) ); graph1.addOutput( iocolourv, crg::makeLayoutState( crg::ImageLayout::eColorAttachment ) ); check( graph1.getOutputLayoutState( colourv ).layout == crg::ImageLayout::eShaderReadOnly ) crg::FrameGraph graph2{ handler, testCounts.testName + "2" }; graph2.addInput( colourv, crg::makeLayoutState( crg::ImageLayout::eShaderReadOnly ) ); graph2.addInput( iocolourv, crg::makeLayoutState( crg::ImageLayout::eColorAttachment ) ); check( graph2.getInputLayoutState( colourv ).layout == crg::ImageLayout::eShaderReadOnly ) graph2.addDependency( graph1 ); auto & testPass2 = graph2.createPass( "Pass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); testPass2.addInputSampled( *coloura, 0u ); testPass2.addInOutColourTarget( *iocoloura ); auto runnable1 = graph1.compile( getContext() ); test::checkRunnable( testCounts, runnable1 ); auto runnable2 = graph2.compile( getContext() ); test::checkRunnable( testCounts, runnable2 ); testEnd() } TEST( Bases, 2PassGraphDeps ) { testBegin( "test2PassGraphDeps" ) crg::ResourceHandler handler; crg::FrameGraph graph1{ handler, testCounts.testName + "1" }; auto buffer = graph1.createBuffer( test::createBuffer( "buffer" ) ); auto bufferv = graph1.createView( test::createView( "bufferv", buffer ) ); auto colour = graph1.createImage( test::createImage( "colour", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colourv = graph1.createView( test::createView( "colourv", colour, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto & testPass11 = graph1.createPass( "Pass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto coloura = testPass11.addOutputColourTarget( colourv ); auto bufferAttach = testPass11.addOutputStorageBuffer( bufferv, 0u ); auto & testPass12 = graph1.createPass( "Pass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); bufferAttach = testPass12.addInOutStorage( *bufferAttach, 0u ); coloura = testPass12.addInOutStorage( *coloura, 1u ); graph1.addOutput( colourv, crg::makeLayoutState( crg::ImageLayout::eShaderReadOnly ) ); check( graph1.getOutputLayoutState( colourv ).layout == crg::ImageLayout::eShaderReadOnly ) crg::FrameGraph graph2{ handler, testCounts.testName + "2" }; graph2.addInput( colourv, crg::makeLayoutState( crg::ImageLayout::eShaderReadOnly ) ); check( graph2.getInputLayoutState( colourv ).layout == crg::ImageLayout::eShaderReadOnly ) graph2.addDependency( graph1 ); { auto & testPass21 = graph2.createPass( "Pass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); testPass21.addInputSampled( *coloura, 0u ); auto & testPass22 = graph2.createPass( "Pass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); testPass22.addInputStorage( *bufferAttach, 0u ); } auto runnable1 = graph1.compile( getContext() ); test::checkRunnable( testCounts, runnable1 ); auto runnable2 = graph2.compile( getContext() ); test::checkRunnable( testCounts, runnable2 ); testEnd() } TEST( Bases, PassGroupDeps ) { testBegin( "testPassGroupDeps" ) crg::ResourceHandler handler; crg::FrameGraph graph1{ handler, testCounts.testName + "1" }; auto & group1 = graph1.getDefaultGroup(); auto colour = group1.createImage( test::createImage( "colour", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colourv = group1.createView( test::createView( "colourv", colour, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto buffer = graph1.createBuffer( test::createBuffer( "buffer" ) ); auto bufferv = graph1.createView( test::createView( "bufferv", buffer ) ); auto & testPass1 = group1.createPass( "Mesh" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto coloura = testPass1.addOutputColourTarget( colourv ); testPass1.addOutputStorageBuffer( bufferv, 0u ); group1.addOutput( colourv, crg::makeLayoutState( crg::ImageLayout::eShaderReadOnly ) ); group1.addGroupOutput( colourv ); crg::FrameGraph graph2{ handler, testCounts.testName + "2" }; auto & group2 = graph2.getDefaultGroup(); group2.addInput( colourv, crg::makeLayoutState( crg::ImageLayout::eShaderReadOnly ) ); group2.addGroupInput( colourv ); graph2.addDependency( graph1 ); auto & testPass2 = group2.createPass( "Pass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); testPass2.addInputSampled( *coloura, 0u ); auto runnable1 = graph1.compile( getContext() ); test::checkRunnable( testCounts, runnable1 ); auto runnable2 = graph2.compile( getContext() ); test::checkRunnable( testCounts, runnable2 ); testEnd() } TEST( Bases, PassGroups ) { testBegin( "testPassGroups" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & group1 = graph.createPassGroup( "First" ); auto colour = group1.createImage( test::createImage( "colour", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colourv = group1.createView( test::createView( "colourv", colour, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto buffer = group1.createBuffer( test::createBuffer( "buffer" ) ); auto bufferv = group1.createView( test::createView( "bufferv", buffer ) ); auto & testPass1 = group1.createPass( "Mesh" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto coloura = testPass1.addOutputColourTarget( colourv ); auto bufferAttach = testPass1.addOutputStorageBuffer( bufferv, 0u ); group1.addGroupOutput( colourv ); auto & group2 = graph.createPassGroup( "Second" ).createPassGroup( "Third" ); group2.addGroupInput( colourv ); auto & testPass2 = group2.createPass( "Pass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); testPass2.addInputSampled( *coloura, 0u ); testPass2.addInputStorage( *bufferAttach, 0u ); auto runnable2 = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable2 ); testEnd() } TEST( Bases, ResourcesCache ) { testBegin( "testResourcesCache" ) { crg::ResourceHandler handler; crg::RecordContext context( handler ); checkThrow( context.getContext(), crg::Exception ) } auto & context = getContext(); crgUnregisterObject( context, VkBuffer( 1 ) ); checkThrow( context.deduceMemoryType( 0u, 0u ), crg::Exception ) { crg::ResourceHandler handler; auto sampled = handler.createImageId( test::createImage( "sampled", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto sampledv = handler.createViewId( test::createView( "sampledv", sampled, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto buffer = handler.createBufferId( test::createBuffer( "buffer" ) ); auto bufferv = handler.createViewId( test::createView( "bufferv", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); handler.createImage( context, sampled ); handler.createImageView( context, sampledv ); handler.createBuffer( context, buffer ); handler.createBufferView( context, bufferv ); handler.createQuadTriVertexBuffer( context, "test", false, {} ); handler.createQuadTriVertexBuffer( context, "test", true, {} ); handler.createSampler( context, "test", crg::SamplerDesc{} ); } crg::ResourceHandler handler; crg::ResourcesCache resources{ handler }; { auto sampled = handler.createImageId( test::createImage( "sampled", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto sampledv = handler.createViewId( test::createView( "sampledv", sampled, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); resources.createImage( context, sampled ); resources.createImageView( context, sampledv ); resources.destroyImageView( context, sampledv ); resources.destroyImage( context, sampled ); auto buffer = handler.createBufferId( test::createBuffer( "buffer" ) ); auto bufferv = handler.createViewId( test::createView( "bufferv", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); resources.createBuffer( context, buffer ); resources.createBufferView( context, bufferv ); resources.destroyBufferView( context, bufferv ); resources.destroyBuffer( context, buffer ); } { auto result = handler.createImageId( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = handler.createViewId( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); resources.createImage( context, result ); resources.createImageView( context, resultv ); resources.destroyImageView( resultv ); resources.destroyImage( result ); auto buffer = handler.createBufferId( test::createBuffer( "resbuffer" ) ); auto bufferv = handler.createViewId( test::createView( "resbufferv", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); resources.createBuffer( context, buffer ); resources.createBufferView( context, bufferv ); resources.destroyBufferView( bufferv ); resources.destroyBuffer( buffer ); } { auto result = handler.createImageId( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = handler.createViewId( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); resources.createImage( context, result ); resources.createImageView( context, resultv ); resources.createSampler( context, crg::SamplerDesc{} ); resources.createQuadTriVertexBuffer( context, false, {} ); resources.createQuadTriVertexBuffer( context, true, {} ); auto buffer = handler.createBufferId( test::createBuffer( "resbuffer" ) ); auto bufferv = handler.createViewId( test::createView( "resbufferv", buffer, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); resources.createBuffer( context, buffer ); resources.createBufferView( context, bufferv ); resources.destroyBufferView( context, bufferv ); resources.destroyBuffer( context, buffer ); } testEnd() } TEST( Bases, GraphNodes ) { testBegin( "testGraphNodes" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; crg::RootNode root{ graph }; check( getFramePass( root ) == nullptr ) auto const & testPass = graph.createPass( "testPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); crg::FramePassNode node{ testPass }; check( getFramePass( node ) == &testPass ) testEnd() } testSuiteMain() ================================================ FILE: test/TestRenderGraph.cpp ================================================ #include "Common.hpp" #include #include #include #include #include #include #include namespace { crg::GraphContext & getContext() { return test::getDummyContext(); } void checkTargetColourIsShaderReadOnly( [[maybe_unused]] test::TestCounts const & testCounts , crg::FramePass const & framePass , crg::RunnableGraph const & , crg::RecordContext const & context , uint32_t index ) { for ( auto attach : framePass.getTargets() ) { auto view = attach->view( index ); if ( attach->isColourOutputImageTarget() ) { auto resolved = crg::resolveView( view, index ); checkEqual( context.getNextLayoutState( resolved ).layout, crg::ImageLayout::eShaderReadOnly ) checkEqual( context.getNextLayoutState( resolved.data->image , resolved.data->info.viewType , getSubresourceRange( resolved ) ).layout, crg::ImageLayout::eShaderReadOnly ) } } } void checkSampledIsShaderReadOnly( [[maybe_unused]] test::TestCounts const & testCounts , crg::FramePass const & framePass , crg::RunnableGraph const & , crg::RecordContext const & context , uint32_t index ) { for ( auto & [binding, attach] : framePass.getSampled() ) { auto view = attach.attach->view( index ); checkEqual( context.getLayoutState( crg::resolveView( view, index ) ).layout, crg::ImageLayout::eShaderReadOnly ) } } crg::FrameGraph buildNoPassGraph( test::TestCounts const & testCounts , crg::ResourceHandler & handler ) { crg::FrameGraph graph{ handler, testCounts.testName }; checkThrow( graph.compile( getContext() ), crg::Exception ) return graph; } } TEST( RenderGraph, NoPass ) { testBegin( "testNoPass" ) crg::ResourceHandler handler; auto graph1 = buildNoPassGraph( testCounts, handler ); crg::FrameGraph graph{ std::move( graph1 ) }; auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv = graph.createView( test::createView( "rtv", rt ) ); auto & pass = graph.createPass( "pass1C" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass.addOutputColourTarget( rtv ); testEnd() } TEST( RenderGraph, OnePass ) { testBegin( "testOnePass" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv = graph.createView( test::createView( "rtv", rt ) ); auto & pass = graph.createPass( "pass1C" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass.addOutputColourTarget( rtv ); auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); std::string ref = R"(digraph { } )"; checkEqualSortedLines( stream, ref ) testEnd() } TEST( RenderGraph, DuplicateName ) { testBegin( "testDuplicateName" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; checkNoThrow( graph.createPass( "pass1C" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ) ) checkThrow( graph.createPass( "pass1C" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ), crg::Exception ) testEnd() } TEST( RenderGraph, OneDependency ) { testBegin( "testOneDependency" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT, 1u, 4u ) ); auto dep = graph.createImage( test::createImage( "dep", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto rtv0 = graph.createView( test::createView( "rtv0", rt, 0u, 1u, 0u, 1u ) ); auto rtv1 = graph.createView( test::createView( "rtv1", rt, 0u, 1u, 1u, 1u ) ); auto rtv2 = graph.createView( test::createView( "rtv2", rt, 0u, 1u, 2u, 1u ) ); auto rtv3 = graph.createView( test::createView( "rtv3", rt, 0u, 1u, 3u, 1u ) ); auto depv = graph.createView( test::createView( "depv", dep ) ); auto buf = graph.createBuffer( test::createBuffer( "buf" ) ); auto bufv = graph.createView( test::createView( "bufv", buf ) ); auto & pass1 = graph.createPass( "pass1C" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass1.addInputUniformBuffer( bufv, 1u ); auto rta = pass1.addOutputColourTarget( graph.mergeViews( { rtv0, rtv1, rtv2, rtv3 } ) ); auto depa = pass1.addOutputDepthStencilTarget( depv ); auto out = graph.createImage( test::createImage( "out", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto outv = graph.createView( test::createView( "outv", out ) ); auto & pass2 = graph.createPass( "pass2C" , []( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return test::createDummyNoRecord( framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass2.addInputSampled( *rta, 0u ); pass2.addInputUniformBuffer( bufv, 1u ); pass2.addOutputColourTarget( outv ); pass2.addInputDepthStencilTarget( *depa ); auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); std::string ref = R"(digraph { "pass1C" [ shape=ellipse ]; "pass2C" [ shape=ellipse ]; "Transition to\npass2C/rt/Spl0" [ shape=box ]; "pass1C" -> "Transition to\npass2C/rt/Spl0" [ label="rtv0" ]; "Transition to\npass2C/rt/Spl0" -> "pass2C" [ label="rtv0" ]; "Transition to\npass2C/rt/Spl1" [ shape=box ]; "pass1C" -> "Transition to\npass2C/rt/Spl1" [ label="rtv1" ]; "Transition to\npass2C/rt/Spl1" -> "pass2C" [ label="rtv1" ]; "Transition to\npass2C/rt/Spl2" [ shape=box ]; "pass1C" -> "Transition to\npass2C/rt/Spl2" [ label="rtv2" ]; "Transition to\npass2C/rt/Spl2" -> "pass2C" [ label="rtv2" ]; "Transition to\npass2C/rt/Spl3" [ shape=box ]; "pass1C" -> "Transition to\npass2C/rt/Spl3" [ label="rtv3" ]; "Transition to\npass2C/rt/Spl3" -> "pass2C" [ label="rtv3" ]; "Transition to\npass2C/depv/IRds" [ shape=box ]; "pass1C" -> "Transition to\npass2C/depv/IRds" [ label="depv" ]; "Transition to\npass2C/depv/IRds" -> "pass2C" [ label="depv" ]; "ExternalSource" [ shape=ellipse ]; "Transition to\npass1C/bufv/UB" [ shape=box ]; "ExternalSource" -> "Transition to\npass1C/bufv/UB" [ label="bufv" ]; "Transition to\npass1C/bufv/UB" -> "pass1C" [ label="bufv" ]; } )"; checkEqualSortedLines( stream, ref ) testEnd() } TEST( RenderGraph, CycleDependency ) { testBegin( "testCycleDependency" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv = graph.createView( test::createView( "rtv", rt ) ); auto & pass1 = graph.createPass( "pass1C" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto rta = pass1.addOutputColourTarget( rtv ); auto out = graph.createImage( test::createImage( "out", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto outv = graph.createView( test::createView( "outv", out ) ); auto & pass2 = graph.createPass( "pass2C" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass2.addInputSampled( *rta, 0u ); auto outa = pass2.addOutputColourTarget( outv ); auto & pass3 = graph.createPass( "pass3C" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass3.addInputSampled( *rta, 0u ); pass3.addInOutColourTarget( *outa ); auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); std::string ref = R"(digraph { "pass1C" [ shape=ellipse ]; "pass2C" [ shape=ellipse ]; "Transition to\npass2C/rtv/Spl" [ shape=box ]; "pass1C" -> "Transition to\npass2C/rtv/Spl" [ label="rtv" ]; "Transition to\npass2C/rtv/Spl" -> "pass2C" [ label="rtv" ]; "pass3C" [ shape=ellipse ]; "Transition to\npass3C/outv/IORcl" [ shape=box ]; "pass2C" -> "Transition to\npass3C/outv/IORcl" [ label="outv" ]; "Transition to\npass3C/outv/IORcl" -> "pass3C" [ label="outv" ]; } )"; checkEqualSortedLines( stream, ref ) testEnd() } TEST( RenderGraph, ChainedDependencies ) { testBegin( "testChainedDependencies" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto d0 = graph.createImage( test::createImage( "d0", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto d0v = graph.createView( test::createView( "d0v", d0 ) ); auto & pass0 = graph.createPass( "pass0" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto d0a = pass0.addOutputColourTarget( d0v ); auto d1 = graph.createImage( test::createImage( "d1", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto d1v = graph.createView( test::createView( "d1v", d1 ) ); auto & pass1 = graph.createPass( "pass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass1.addInputSampled( *d0a, 0u ); auto d1a = pass1.addOutputColourTarget( d1v ); auto buf = graph.createBuffer( test::createBuffer( "buf" ) ); auto bufv = graph.createView( test::createView( "bufv", buf ) ); auto & pass2 = graph.createPass( "pass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass2.addInputSampled( *d1a, 0u ); pass2.addInputUniformBuffer( bufv, 1u ); pass2.addInputColourTarget( *d1a ); auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); std::string ref = R"(digraph { "pass1" [ shape=ellipse ]; "pass2" [ shape=ellipse ]; "Transition to\npass2/d1v/Spl" [ shape=box ]; "pass1" -> "Transition to\npass2/d1v/Spl" [ label="d1v" ]; "Transition to\npass2/d1v/Spl" -> "pass2" [ label="d1v" ]; "Transition to\npass2/d1v/IRcl" [ shape=box ]; "pass1" -> "Transition to\npass2/d1v/IRcl" [ label="d1v" ]; "Transition to\npass2/d1v/IRcl" -> "pass2" [ label="d1v" ]; "pass0" [ shape=ellipse ]; "Transition to\npass1/d0v/Spl" [ shape=box ]; "pass0" -> "Transition to\npass1/d0v/Spl" [ label="d0v" ]; "Transition to\npass1/d0v/Spl" -> "pass1" [ label="d0v" ]; "ExternalSource" [ shape=ellipse ]; "Transition to\npass2/bufv/UB" [ shape=box ]; "ExternalSource" -> "Transition to\npass2/bufv/UB" [ label="bufv" ]; "Transition to\npass2/bufv/UB" -> "pass2" [ label="bufv" ]; } )"; checkEqualSortedLines( stream, ref ) testEnd() } TEST( RenderGraph, SharedDependencies ) { testBegin( "testSharedDependencies" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto d = graph.createImage( test::createImage( "d", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto dstv1 = graph.createView( test::createView( "dstv1", d ) ); auto buf = graph.createBuffer( test::createBuffer( "buf" ) ); auto bufv1 = graph.createView( test::createView( "bufv1", buf ) ); auto d0 = graph.createImage( test::createImage( "d0", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto d0v = graph.createView( test::createView( "d0v", d0 ) ); auto & pass0 = graph.createPass( "pass0" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto attach = pass0.addOutputStorageBuffer( bufv1, 0 ); pass0.addOutputDepthTarget( dstv1 ); auto d0a = pass0.addOutputColourTarget( d0v ); auto dstv2 = graph.createView( test::createView( "dstv2", d ) ); auto bufv2 = graph.createView( test::createView( "bufv2", buf ) ); auto d1 = graph.createImage( test::createImage( "d1", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto d1v = graph.createView( test::createView( "d1v", d1 ) ); auto & pass1 = graph.createPass( "pass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass1.addInputSampled( *d0a, 0 ); pass0.addOutputStorageBuffer( bufv2, 0 ); auto dsta = pass1.addOutputDepthTarget ( dstv2 ); auto d1a = pass1.addOutputColourTarget( d1v ); auto d2 = graph.createImage( test::createImage( "d2", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto d2v = graph.createView( test::createView( "d2v", d2 ) ); auto & pass2 = graph.createPass( "pass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass2.addInputSampled( *d1a, 0 ); pass2.addInputDepthTarget( *dsta ); pass2.addOutputColourTarget( d2v ); auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); runnable->getDescriptorWriteT( *attach, 0u ); runnable->getContext(); { crg::RunnableGraph const & rn = *runnable; auto const & fence = rn.getFence(); fence.getInternal(); auto const & timer = rn.getTimer(); timer.getName(); } { crg::RunnableGraph & rn = *runnable; auto & fence = rn.getFence(); fence.reset(); auto & timer = rn.getTimer(); timer.reset(); } check( runnable->getName() == graph.getName() ) std::string ref = R"(digraph { "pass2" [ shape=ellipse ]; "pass1" [ shape=ellipse ]; "Transition to\npass2/d1v/Spl" [ shape=box ]; "pass1" -> "Transition to\npass2/d1v/Spl" [ label="d1v" ]; "Transition to\npass2/d1v/Spl" -> "pass2" [ label="d1v" ]; "Transition to\npass2/dstv1/IRdp" [ shape=box ]; "pass1" -> "Transition to\npass2/dstv1/IRdp" [ label="dstv1" ]; "Transition to\npass2/dstv1/IRdp" -> "pass2" [ label="dstv1" ]; "pass0" [ shape=ellipse ]; "Transition to\npass1/d0v/Spl" [ shape=box ]; "pass0" -> "Transition to\npass1/d0v/Spl" [ label="d0v" ]; "Transition to\npass1/d0v/Spl" -> "pass1" [ label="d0v" ]; } )"; checkEqualSortedLines( stream, ref ) testEnd() } TEST( RenderGraph, 2MipDependencies ) { testBegin( "test2MipDependencies" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto lp = graph.createImage( test::createImage( "lp", crg::PixelFormat::eR32G32B32_SFLOAT, 3u ) ); auto m0v = graph.createView( test::createView( "m0v", lp, 0u ) ); auto m1v = graph.createView( test::createView( "m1v", lp, 1u ) ); auto m0a = crg::Attachment::createDefault( m0v ); graph.addInput( m0v, makeLayoutState( crg::ImageLayout::eShaderReadOnly ) ); auto & ssaoMinifyPass1 = graph.createPass( "ssaoMinifyPass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoMinifyPass1.addInputSampled( m0a, 0 ); auto m1a = ssaoMinifyPass1.addOutputColourTarget( m1v ); auto m2v = graph.createView( test::createView( "m2v", lp, 2u ) ); auto & ssaoMinifyPass2 = graph.createPass( "ssaoMinifyPass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoMinifyPass2.addInputSampled( *m1a, 0 ); ssaoMinifyPass2.addOutputColourTarget( m2v ); auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); std::string ref = R"(digraph { "ssaoMinifyPass1" [ shape=ellipse ]; "ssaoMinifyPass2" [ shape=ellipse ]; "Transition to\nssaoMinifyPass2/m1v/Spl" [ shape=box ]; "ssaoMinifyPass1" -> "Transition to\nssaoMinifyPass2/m1v/Spl" [ label="m1v" ]; "Transition to\nssaoMinifyPass2/m1v/Spl" -> "ssaoMinifyPass2" [ label="m1v" ]; "ExternalSource" [ shape=ellipse ]; "Transition to\nssaoMinifyPass1/m0v/Spl" [ shape=box ]; "ExternalSource" -> "Transition to\nssaoMinifyPass1/m0v/Spl" [ label="m0v" ]; "Transition to\nssaoMinifyPass1/m0v/Spl" -> "ssaoMinifyPass1" [ label="m0v" ]; } )"; checkEqualSortedLines( stream, ref ) testEnd() } TEST( RenderGraph, 3MipDependencies ) { testBegin( "test3MipDependencies" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto lp = graph.createImage( test::createImage( "lp", crg::PixelFormat::eR32G32B32_SFLOAT, 4u ) ); auto m0v = graph.createView( test::createView( "m0v", lp, crg::PixelFormat::eR32G32B32_SFLOAT, 0u ) ); auto m1v = graph.createView( test::createView( "m1v", lp, crg::PixelFormat::eR32G32B32_SFLOAT, 1u ) ); auto m0a = crg::Attachment::createDefault( m0v ); auto & ssaoMinifyPass1 = graph.createPass( "ssaoMinifyPass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoMinifyPass1.addInputSampled( m0a, 0 ); auto m1a = ssaoMinifyPass1.addOutputColourTarget( m1v ); auto m2v = graph.createView( test::createView( "m2v", lp, crg::PixelFormat::eR32G32B32_SFLOAT, 2u ) ); auto & ssaoMinifyPass2 = graph.createPass( "ssaoMinifyPass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoMinifyPass2.addInputSampled( *m1a, 0 ); auto m2a = ssaoMinifyPass2.addOutputColourTarget( m2v ); auto m3v = graph.createView( test::createView( "m3v", lp, crg::PixelFormat::eR32G32B32_SFLOAT, 3u ) ); auto & ssaoMinifyPass3 = graph.createPass( "ssaoMinifyPass3" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoMinifyPass3.addInputSampled( *m2a, 0 ); ssaoMinifyPass3.addOutputColourTarget( m3v ); auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); std::string ref = R"(digraph { "ssaoMinifyPass2" [ shape=ellipse ]; "ssaoMinifyPass3" [ shape=ellipse ]; "Transition to\nssaoMinifyPass3/m2v/Spl" [ shape=box ]; "ssaoMinifyPass2" -> "Transition to\nssaoMinifyPass3/m2v/Spl" [ label="m2v" ]; "Transition to\nssaoMinifyPass3/m2v/Spl" -> "ssaoMinifyPass3" [ label="m2v" ]; "ssaoMinifyPass1" [ shape=ellipse ]; "Transition to\nssaoMinifyPass2/m1v/Spl" [ shape=box ]; "ssaoMinifyPass1" -> "Transition to\nssaoMinifyPass2/m1v/Spl" [ label="m1v" ]; "Transition to\nssaoMinifyPass2/m1v/Spl" -> "ssaoMinifyPass2" [ label="m1v" ]; "ExternalSource" [ shape=ellipse ]; "Transition to\nssaoMinifyPass1/m0v/Spl" [ shape=box ]; "ExternalSource" -> "Transition to\nssaoMinifyPass1/m0v/Spl" [ label="m0v" ]; "Transition to\nssaoMinifyPass1/m0v/Spl" -> "ssaoMinifyPass1" [ label="m0v" ]; } )"; checkEqualSortedLines( stream, ref ) testEnd() } TEST( RenderGraph, LoopDependencies ) { testBegin( "testLoopDependencies" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto a = graph.createImage( test::createImage( "a", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto av = graph.createView( test::createView( "av", a, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto b = graph.createImage( test::createImage( "b", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto bv = graph.createView( test::createView( "bv", b, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto ba = crg::Attachment::createDefault( bv ); auto & pass1 = graph.createPass( "pass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass1.addInputSampled( ba, 0 ); auto aa = pass1.addOutputColourTarget( av ); auto & pass2 = graph.createPass( "pass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass2.addInputSampled( *aa, 0 ); pass2.addOutputColourTarget( bv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( RenderGraph, LoopDependenciesWithRoot ) { testBegin( "testLoopDependenciesWithRoot" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto b = graph.createImage( test::createImage( "b", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto bv = graph.createView( test::createView( "bv", b, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto & pass0 = graph.createPass( "pass0" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto ba = pass0.addOutputColourTarget( bv ); auto a = graph.createImage( test::createImage( "a", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto av = graph.createView( test::createView( "av", a, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto & pass1 = graph.createPass( "pass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass1.addInputSampled( *ba, 0 ); auto aa = pass1.addOutputColourTarget( av ); auto & pass2 = graph.createPass( "pass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass2.addInputSampled( *aa, 0 ); pass2.addOutputColourTarget( bv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( RenderGraph, LoopDependenciesWithRootAndLeaf ) { testBegin( "testLoopDependenciesWithRootAndLeaf" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto c = graph.createImage( test::createImage( "c", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto cv = graph.createView( test::createView( "cv", c, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto & pass0 = graph.createPass( "pass0" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto ca = pass0.addOutputColourTarget( cv ); auto a = graph.createImage( test::createImage( "a", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto av = graph.createView( test::createView( "av", a, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto b = graph.createImage( test::createImage( "b", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto bv = graph.createView( test::createView( "bv", b, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto ba = crg::Attachment::createDefault( bv ); auto & pass1 = graph.createPass( "pass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass1.addInputSampled( ba, 0 ); pass1.addInputSampled( *ca, 1 ); auto aa = pass1.addOutputColourTarget( av ); auto & pass2 = graph.createPass( "pass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass2.addInputSampled( *aa, 0 ); pass2.addInputSampled( *ca, 1 ); pass2.addOutputColourTarget( bv ); auto buf = graph.createBuffer( test::createBuffer( "buf" ) ); auto bufv = graph.createView( test::createView( "bufv", buf ) ); auto & pass3 = graph.createPass( "pass3" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); pass3.addInputSampled( *ca, 0 ); pass3.addInputUniformBuffer( bufv, 1 ); auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); std::string ref = R"(digraph { "pass1" [ shape=ellipse ]; "pass2" [ shape=ellipse ]; "Transition to\npass2/av/Spl" [ shape=box ]; "pass1" -> "Transition to\npass2/av/Spl" [ label="av" ]; "Transition to\npass2/av/Spl" -> "pass2" [ label="av" ]; "pass0" [ shape=ellipse ]; "Transition to\npass1/cv/Spl" [ shape=box ]; "pass0" -> "Transition to\npass1/cv/Spl" [ label="cv" ]; "Transition to\npass1/cv/Spl" -> "pass1" [ label="cv" ]; "pass3" [ shape=ellipse ]; "ExternalSource" [ shape=ellipse ]; "Transition to\npass1/bv/Spl" [ shape=box ]; "ExternalSource" -> "Transition to\npass1/bv/Spl" [ label="bv" ]; "Transition to\npass1/bv/Spl" -> "pass1" [ label="bv" ]; "Transition to\npass3/bufv/UB" [ shape=box ]; "ExternalSource" -> "Transition to\npass3/bufv/UB" [ label="bufv" ]; "Transition to\npass3/bufv/UB" -> "pass3" [ label="bufv" ]; } )"; checkEqualSortedLines( stream, ref ) testEnd() } crg::Attachment const * buildSsaoPass( test::TestCounts & testCounts , crg::Attachment const & lda , crg::FrameGraph & graph ) { auto lp = graph.createImage( test::createImage( "lp", crg::PixelFormat::eR32_SFLOAT, 4u ) ); auto m0v = graph.createView( test::createView( "m0v", lp, crg::PixelFormat::eR32_SFLOAT, 0u ) ); auto & ssaoLinearisePass = graph.createPass( "ssaoLinearisePass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoLinearisePass.addInputSampled( lda, 0 ); auto m0a = ssaoLinearisePass.addOutputColourTarget( m0v ); auto m1v = graph.createView( test::createView( "m1v", lp, crg::PixelFormat::eR32_SFLOAT, 1u ) ); auto & ssaoMinifyPass1 = graph.createPass( "ssaoMinifyPass1" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoMinifyPass1.addInputSampled( *m0a, 0 ); auto m1a = ssaoMinifyPass1.addOutputColourTarget( m1v ); auto m2v = graph.createView( test::createView( "m2v", lp, crg::PixelFormat::eR32G32B32_SFLOAT, 2u ) ); auto & ssaoMinifyPass2 = graph.createPass( "ssaoMinifyPass2" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoMinifyPass2.addInputSampled( *m1a, 0 ); auto m2a = ssaoMinifyPass2.addOutputColourTarget( m2v ); auto m3v = graph.createView( test::createView( "m3v", lp, crg::PixelFormat::eR32G32B32_SFLOAT, 3u ) ); auto & ssaoMinifyPass3 = graph.createPass( "ssaoMinifyPass3" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoMinifyPass3.addInputSampled( *m2a, 0 ); auto m3a = ssaoMinifyPass3.addOutputColourTarget( m3v ); auto rs = graph.createImage( test::createImage( "rs", crg::PixelFormat::eR32_SFLOAT ) ); auto rsv = graph.createView( test::createView( "rsv", rs, crg::PixelFormat::eR32_SFLOAT ) ); auto & ssaoRawPass = graph.createPass( "ssaoRawPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoRawPass.addInputSampled( *graph.mergeAttachments( { m0a, m1a, m2a, m3a } ), 0 ); ssaoRawPass.addInputSampled( *m3a, 1 ); auto rsa = ssaoRawPass.addOutputColourTarget( rsv ); auto bl = graph.createImage( test::createImage( "b1", crg::PixelFormat::eR32_SFLOAT ) ); auto blv = graph.createView( test::createView( "b1v", bl, crg::PixelFormat::eR32_SFLOAT ) ); auto & ssaoBlurPass = graph.createPass( "ssaoBlurPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ssaoBlurPass.addInputSampled( *rsa, 0 ); ssaoBlurPass.addInputSampled( *m3a, 1 ); return ssaoBlurPass.addOutputColourTarget( blv ); } TEST( RenderGraph, SsaoPass ) { testBegin( "testSsaoPass" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto d = graph.createImage( test::createImage( "d", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto dtv = graph.createView( test::createView( "dtv", d, crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto ld = graph.createImage( test::createImage( "ld", crg::PixelFormat::eR32_SFLOAT ) ); auto ldv = graph.createView( test::createView( "ldv", ld, crg::PixelFormat::eR32_SFLOAT ) ); auto v = graph.createImage( test::createImage( "v", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto vv = graph.createView( test::createView( "vv", v, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d1 = graph.createImage( test::createImage( "d1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto d1v = graph.createView( test::createView( "d1v", d1, crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto d2 = graph.createImage( test::createImage( "d2", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d2v = graph.createView( test::createView( "d2v", d2, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d3 = graph.createImage( test::createImage( "d3", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d3v = graph.createView( test::createView( "d3v", d3, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d4 = graph.createImage( test::createImage( "d4", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d4v = graph.createView( test::createView( "d4v", d4, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto & geometryPass = graph.createPass( "geometryPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto d1a = geometryPass.addOutputColourTarget( d1v ); auto d2a = geometryPass.addOutputColourTarget( d2v ); auto d3a = geometryPass.addOutputColourTarget( d3v ); auto d4a = geometryPass.addOutputColourTarget( d4v ); auto lda = geometryPass.addOutputColourTarget( ldv ); geometryPass.addOutputColourTarget( vv ); geometryPass.addOutputDepthStencilTarget( dtv ); auto ssaoa = buildSsaoPass( testCounts , *lda , graph ); auto of = graph.createImage( test::createImage( "of", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto ofv = graph.createView( test::createView( "ofv", of, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto & ambientPass = graph.createPass( "ambientPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ambientPass.addInputSampled( *lda, 0 ); ambientPass.addInputSampled( *d1a, 1 ); ambientPass.addInputSampled( *d2a, 2 ); ambientPass.addInputSampled( *d3a, 3 ); ambientPass.addInputSampled( *d4a, 4 ); ambientPass.addInputSampled( *ssaoa, 5 ); ambientPass.addOutputColourTarget( ofv ); auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); std::string ref = R"(digraph { "ambientPass" [ shape=ellipse ]; "geometryPass" [ shape=ellipse ]; "Transition to\nambientPass/d1v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nambientPass/d1v/Spl" [ label="d1v" ]; "Transition to\nambientPass/d1v/Spl" -> "ambientPass" [ label="d1v" ]; "Transition to\nambientPass/d2v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nambientPass/d2v/Spl" [ label="d2v" ]; "Transition to\nambientPass/d2v/Spl" -> "ambientPass" [ label="d2v" ]; "Transition to\nambientPass/d3v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nambientPass/d3v/Spl" [ label="d3v" ]; "Transition to\nambientPass/d3v/Spl" -> "ambientPass" [ label="d3v" ]; "Transition to\nambientPass/d4v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nambientPass/d4v/Spl" [ label="d4v" ]; "Transition to\nambientPass/d4v/Spl" -> "ambientPass" [ label="d4v" ]; "ssaoBlurPass" [ shape=ellipse ]; "Transition to\nambientPass/b1v/Spl" [ shape=box ]; "ssaoBlurPass" -> "Transition to\nambientPass/b1v/Spl" [ label="b1v" ]; "Transition to\nambientPass/b1v/Spl" -> "ambientPass" [ label="b1v" ]; "ssaoRawPass" [ shape=ellipse ]; "Transition to\nssaoBlurPass/rsv/Spl" [ shape=box ]; "ssaoRawPass" -> "Transition to\nssaoBlurPass/rsv/Spl" [ label="rsv" ]; "Transition to\nssaoBlurPass/rsv/Spl" -> "ssaoBlurPass" [ label="rsv" ]; "ssaoMinifyPass3" [ shape=ellipse ]; "Transition to\nssaoRawPass/lp/Spl3" [ shape=box ]; "ssaoMinifyPass3" -> "Transition to\nssaoRawPass/lp/Spl3" [ label="m3v" ]; "Transition to\nssaoRawPass/lp/Spl3" -> "ssaoRawPass" [ label="m3v" ]; "ssaoMinifyPass2" [ shape=ellipse ]; "Transition to\nssaoMinifyPass3/m2v/Spl" [ shape=box ]; "ssaoMinifyPass2" -> "Transition to\nssaoMinifyPass3/m2v/Spl" [ label="m2v" ]; "Transition to\nssaoMinifyPass3/m2v/Spl" -> "ssaoMinifyPass3" [ label="m2v" ]; "ssaoMinifyPass1" [ shape=ellipse ]; "Transition to\nssaoMinifyPass2/m1v/Spl" [ shape=box ]; "ssaoMinifyPass1" -> "Transition to\nssaoMinifyPass2/m1v/Spl" [ label="m1v" ]; "Transition to\nssaoMinifyPass2/m1v/Spl" -> "ssaoMinifyPass2" [ label="m1v" ]; "ssaoLinearisePass" [ shape=ellipse ]; "Transition to\nssaoMinifyPass1/m0v/Spl" [ shape=box ]; "ssaoLinearisePass" -> "Transition to\nssaoMinifyPass1/m0v/Spl" [ label="m0v" ]; "Transition to\nssaoMinifyPass1/m0v/Spl" -> "ssaoMinifyPass1" [ label="m0v" ]; "Transition to\nssaoLinearisePass/ldv/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nssaoLinearisePass/ldv/Spl" [ label="ldv" ]; "Transition to\nssaoLinearisePass/ldv/Spl" -> "ssaoLinearisePass" [ label="ldv" ]; } )"; checkEqualSortedLines( stream, ref ) testEnd() } TEST( RenderGraph, BloomPostEffect ) { testBegin( "testBloomPostEffect" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto scene = graph.createImage( test::createImage( "scene", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto scenev = graph.createView( test::createView( "scenev", scene, crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto scenea = crg::Attachment::createDefault( scenev ); auto output = graph.createImage( test::createImage( "output", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto outputv = graph.createView( test::createView( "outputv", output, crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto hi = graph.createImage( test::createImage( "hi", crg::PixelFormat::eR32G32B32A32_SFLOAT, 4u ) ); auto hi0v = graph.createView( test::createView( "hi0v", hi, crg::PixelFormat::eR32G32B32A32_SFLOAT, 0u ) ); auto hi1v = graph.createView( test::createView( "hi1v", hi, crg::PixelFormat::eR32G32B32A32_SFLOAT, 1u ) ); auto hi2v = graph.createView( test::createView( "hi2v", hi, crg::PixelFormat::eR32G32B32A32_SFLOAT, 2u ) ); auto hi3v = graph.createView( test::createView( "hi3v", hi, crg::PixelFormat::eR32G32B32A32_SFLOAT, 3u ) ); auto bl = graph.createImage( test::createImage( "bl", crg::PixelFormat::eR32G32B32A32_SFLOAT, 4u ) ); auto bl0v = graph.createView( test::createView( "bl0v", bl, crg::PixelFormat::eR32G32B32A32_SFLOAT, 0u ) ); auto bl1v = graph.createView( test::createView( "bl1v", bl, crg::PixelFormat::eR32G32B32A32_SFLOAT, 1u ) ); auto bl2v = graph.createView( test::createView( "bl2v", bl, crg::PixelFormat::eR32G32B32A32_SFLOAT, 2u ) ); auto bl3v = graph.createView( test::createView( "bl3v", bl, crg::PixelFormat::eR32G32B32A32_SFLOAT, 3u ) ); auto & hiPass = graph.createPass( "hiPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); hiPass.addInputSampled( scenea, 0u ); auto hia = hiPass.addOutputColourTarget( graph.mergeViews( { hi0v, hi1v, hi2v, hi3v } ) ); require( hia->source.size() == 4u ); auto & blurPass0X = graph.createPass( "blurPass0X" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); blurPass0X.addInputSampled( *hia->getSource( 0 ), 0u ); auto bl0a = blurPass0X.addOutputColourTarget( bl0v ); auto & blurPass0Y = graph.createPass( "blurPass0Y" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); blurPass0Y.addInputSampled( *bl0a, 0u ); auto hi0a = blurPass0Y.addOutputColourTarget( hi0v ); auto & blurPass1X = graph.createPass( "blurPass1X" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); blurPass1X.addInputSampled( *hia->getSource( 1 ), 0u ); auto bl1a = blurPass1X.addOutputColourTarget( bl1v ); auto & blurPass1Y = graph.createPass( "blurPass1Y" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); blurPass1Y.addInputSampled( *bl1a, 0u ); auto hi1a = blurPass1Y.addOutputColourTarget( hi1v ); auto & blurPass2X = graph.createPass( "blurPass2X" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); blurPass2X.addInputSampled( *hia->getSource( 2 ), 0u ); auto bl2a = blurPass2X.addOutputColourTarget( bl2v ); auto & blurPass2Y = graph.createPass( "blurPass2Y" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); blurPass2Y.addInputSampled( *bl2a, 0u ); auto hi2a = blurPass2Y.addOutputColourTarget( hi2v ); auto & blurPass3X = graph.createPass( "blurPass3X" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); blurPass3X.addInputSampled( *hia->getSource( 3 ), 0u ); auto bl3a = blurPass3X.addOutputColourTarget( bl3v ); auto & blurPass3Y = graph.createPass( "blurPass3Y" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); blurPass3Y.addInputSampled( *bl3a, 0u ); auto hi3a = blurPass3Y.addOutputColourTarget( hi3v ); auto & combinePass = graph.createPass( "combinePass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); combinePass.addInputSampled( scenea, 0u ); combinePass.addInputSampled( *graph.mergeAttachments( { hi0a, hi1a, hi2a, hi3a } ), 1u ); combinePass.addOutputColourTarget( outputv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } template< bool EnableSsao > crg::Attachment const * buildDeferred( test::TestCounts & testCounts , crg::Attachment const *& lda , crg::Attachment const *& dta , crg::ImageViewId const & ldv , crg::ImageViewId const & dtv , crg::ImageViewId const & vtv , crg::FrameGraph & graph ) { auto d1 = graph.createImage( test::createImage( "d1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto d1v = graph.createView( test::createView( "d1v", d1, crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto d2 = graph.createImage( test::createImage( "d2", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d2v = graph.createView( test::createView( "d2v", d2, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d3 = graph.createImage( test::createImage( "d3", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d3v = graph.createView( test::createView( "d3v", d3, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d4 = graph.createImage( test::createImage( "d4", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto d4v = graph.createView( test::createView( "d4v", d4, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto & geometryPass = graph.createPass( "geometryPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto d1a = geometryPass.addOutputColourTarget( d1v ); auto d2a = geometryPass.addOutputColourTarget( d2v ); auto d3a = geometryPass.addOutputColourTarget( d3v ); auto d4a = geometryPass.addOutputColourTarget( d4v ); if ( !lda ) lda = geometryPass.addOutputColourTarget( ldv ); geometryPass.addOutputColourTarget( vtv ); if ( dta ) geometryPass.addInputDepthStencilTarget( *dta ); else dta = geometryPass.addOutputDepthStencilTarget( dtv ); auto df = graph.createImage( test::createImage( "df", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto dfv = graph.createView( test::createView( "dfv", df, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto sp = graph.createImage( test::createImage( "sp", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto spv = graph.createView( test::createView( "spv", sp, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto & lightingPass = graph.createPass( "lightingPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); lightingPass.addInputSampled( *lda, 0 ); lightingPass.addInputSampled( *d1a, 1 ); lightingPass.addInputSampled( *d2a, 2 ); lightingPass.addInputSampled( *d3a, 3 ); lightingPass.addInputSampled( *d4a, 4 ); auto dfa = lightingPass.addOutputColourTarget( dfv ); auto spa = lightingPass.addOutputColourTarget( spv ); auto of = graph.createImage( test::createImage( "of", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto ofv = graph.createView( test::createView( "ofv", of, crg::PixelFormat::eR32G32B32_SFLOAT ) ); if constexpr ( EnableSsao ) { auto ssaoa = buildSsaoPass( testCounts , *lda , graph ); auto & ambientPass = graph.createPass( "ambientPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ambientPass.addInputSampled( *lda, 0 ); ambientPass.addInputSampled( *d1a, 1 ); ambientPass.addInputSampled( *d2a, 2 ); ambientPass.addInputSampled( *d3a, 3 ); ambientPass.addInputSampled( *d4a, 4 ); ambientPass.addInputSampled( *dfa, 5 ); ambientPass.addInputSampled( *spa, 6 ); ambientPass.addInputSampled( *ssaoa, 7 ); return ambientPass.addOutputColourTarget( ofv ); } else { auto & ambientPass = graph.createPass( "ambientPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); ambientPass.addInputSampled( *lda, 0 ); ambientPass.addInputSampled( *d1a, 1 ); ambientPass.addInputSampled( *d2a, 2 ); ambientPass.addInputSampled( *d3a, 3 ); ambientPass.addInputSampled( *d4a, 4 ); ambientPass.addInputSampled( *dfa, 5 ); ambientPass.addInputSampled( *spa, 6 ); return ambientPass.addOutputColourTarget( ofv ); } } crg::Attachment const * buildWeightedBlended( test::TestCounts & testCounts , crg::Attachment const *& dta , crg::Attachment const * lda , crg::ImageViewId const & dtv , crg::FrameGraph & graph ) { auto a = graph.createImage( test::createImage( "a", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto av = graph.createView( test::createView( "av", a, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto r = graph.createImage( test::createImage( "r", crg::PixelFormat::eR16_SFLOAT ) ); auto rv = graph.createView( test::createView( "rv", r, crg::PixelFormat::eR16_SFLOAT ) ); auto & accumulationPass = graph.createPass( "accumulationPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto aa = accumulationPass.addOutputColourTarget( av ); auto ra = accumulationPass.addOutputColourTarget( rv ); if ( dta ) accumulationPass.addInputDepthStencilTarget( *dta ); else dta = accumulationPass.addOutputDepthStencilTarget( dtv ); auto c = graph.createImage( test::createImage( "c", crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto cv = graph.createView( test::createView( "cv", c, crg::PixelFormat::eR32G32B32_SFLOAT ) ); auto & combinePass = graph.createPass( "combinePass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); if ( lda ) combinePass.addInputSampled( *lda, 0u ); combinePass.addInputSampled( *aa, 1u ); combinePass.addInputSampled( *ra, 2u ); return combinePass.addOutputColourTarget( cv ); } template< bool EnableDepthPrepass , bool EnableOpaque , bool EnableSsao , bool EnableTransparent > struct ParamsT { static constexpr bool EnableDepthPrepassT = EnableDepthPrepass; static constexpr bool EnableOpaqueT = EnableOpaque; static constexpr bool EnableSsaoT = EnableSsao; static constexpr bool EnableTransparentT = EnableTransparent; }; using ParamTypes = testing::Types< ParamsT< false, false, false, false > , ParamsT< false, false, false, true > , ParamsT< false, true, false, false > , ParamsT< false, true, false, true > , ParamsT< false, true, true, false > , ParamsT< false, true, true, true > , ParamsT< true, false, false, false > , ParamsT< true, false, false, true > , ParamsT< true, true, false, false > , ParamsT< true, true, false, true > , ParamsT< true, true, true, false > , ParamsT< true, true, true, true > >; class ParamTypeNames { public: template< typename T > static std::string GetName( int ) { static constexpr bool EnableDepthPrepass = T::EnableDepthPrepassT; static constexpr bool EnableOpaque = T::EnableOpaqueT; static constexpr bool EnableSsao = T::EnableSsaoT; static constexpr bool EnableTransparent = T::EnableTransparentT; return ( EnableDepthPrepass ? std::string{ "Prepass" } : std::string{} ) + ( EnableOpaque ? std::string{ "Opaque" } : std::string{} ) + ( EnableSsao ? std::string{ "Ssao" } : std::string{} ) + ( EnableTransparent ? std::string{ "Transparent" } : std::string{} ); } }; template< typename ParamT > struct RenderGraphT : public ::testing::Test { }; TYPED_TEST_SUITE( RenderGraphT, ParamTypes, ParamTypeNames ); TYPED_TEST( RenderGraphT, Render ) { static constexpr bool EnableDepthPrepass = TypeParam::EnableDepthPrepassT; static constexpr bool EnableOpaque = TypeParam::EnableOpaqueT; static constexpr bool EnableSsao = TypeParam::EnableSsaoT; static constexpr bool EnableTransparent = TypeParam::EnableTransparentT; testBegin( "testRender" + ( EnableDepthPrepass ? std::string{ "Prepass" } : std::string{} ) + ( EnableOpaque ? std::string{ "Opaque" } : std::string{} ) + ( EnableSsao ? std::string{ "Ssao" } : std::string{} ) + ( EnableTransparent ? std::string{ "Transparent" } : std::string{} ) ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto d = graph.createImage( test::createImage( "d", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto dtv = graph.createView( test::createView( "dtv", d, crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto ld = graph.createImage( test::createImage( "ld", crg::PixelFormat::eR32_SFLOAT ) ); auto ldv = graph.createView( test::createView( "ldv", ld, crg::PixelFormat::eR32_SFLOAT ) ); crg::Attachment const * dta{}; crg::Attachment const * lda{}; if constexpr ( EnableDepthPrepass ) { auto & depthPrepass = graph.createPass( "depthPrepass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); lda = depthPrepass.addOutputColourTarget ( ldv ); dta = depthPrepass.addOutputDepthTarget ( dtv ); } auto o = graph.createImage( test::createImage( "o", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto otv = graph.createView( test::createView( "otv", o, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); if constexpr ( EnableOpaque ) { auto v = graph.createImage( test::createImage( "v", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto vv = graph.createView( test::createView( "vv", v, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto dca = buildDeferred< EnableSsao >( testCounts , lda , dta , ldv , dtv , vv , graph ); if constexpr ( EnableTransparent ) { auto wbcsa = buildWeightedBlended( testCounts , dta , lda , dtv , graph ); auto & finalCombinePass = graph.createPass( "finalCombinePass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); finalCombinePass.addInputSampled( *lda, 0 ); finalCombinePass.addInputSampled( *dca, 1 ); finalCombinePass.addInputSampled( *wbcsa, 2 ); finalCombinePass.addOutputColourTarget( otv ); } else { auto & finalCombinePass = graph.createPass( "finalCombinePass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); finalCombinePass.addInputSampled( *lda, 0 ); finalCombinePass.addInputSampled( *dca, 1 ); finalCombinePass.addOutputColourTarget( otv ); } } else if constexpr ( EnableTransparent ) { auto wba = buildWeightedBlended( testCounts , dta , lda , dtv , graph ); auto & finalCombinePass = graph.createPass( "finalCombinePass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); if ( lda ) finalCombinePass.addInputSampled( *lda, 0 ); finalCombinePass.addInputSampled( *wba, 1 ); finalCombinePass.addOutputColourTarget( otv ); } else { auto & finalCombinePass = graph.createPass( "finalCombinePass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); if ( lda ) finalCombinePass.addInputSampled( *lda, 0 ); finalCombinePass.addOutputColourTarget( otv ); } auto runnable = graph.compile( getContext() ); auto stream = test::checkRunnable( testCounts, runnable ); std::string ref; if constexpr ( EnableDepthPrepass ) { if constexpr ( EnableOpaque ) { if constexpr ( EnableSsao ) { if constexpr ( EnableTransparent ) { ref = R"(digraph { "depthPrepass" [ shape=ellipse ]; "geometryPass" [ shape=ellipse ]; "Transition to\ngeometryPass/dtv/IRds" [ shape=box ]; "depthPrepass" -> "Transition to\ngeometryPass/dtv/IRds" [ label="dtv" ]; "Transition to\ngeometryPass/dtv/IRds" -> "geometryPass" [ label="dtv" ]; "ambientPass" [ shape=ellipse ]; "finalCombinePass" [ shape=ellipse ]; "combinePass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/ofv/Spl" [ shape=box ]; "ambientPass" -> "Transition to\nfinalCombinePass/ofv/Spl" [ label="ofv" ]; "Transition to\nfinalCombinePass/ofv/Spl" -> "finalCombinePass" [ label="ofv" ]; "Transition to\nfinalCombinePass/cv/Spl" [ shape=box ]; "combinePass" -> "Transition to\nfinalCombinePass/cv/Spl" [ label="cv" ]; "Transition to\nfinalCombinePass/cv/Spl" -> "finalCombinePass" [ label="cv" ]; "lightingPass" [ shape=ellipse ]; "ssaoBlurPass" [ shape=ellipse ]; "Transition to\nambientPass/dfv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/dfv/Spl" [ label="dfv" ]; "Transition to\nambientPass/dfv/Spl" -> "ambientPass" [ label="dfv" ]; "Transition to\nambientPass/spv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/spv/Spl" [ label="spv" ]; "Transition to\nambientPass/spv/Spl" -> "ambientPass" [ label="spv" ]; "Transition to\nambientPass/b1v/Spl" [ shape=box ]; "ssaoBlurPass" -> "Transition to\nambientPass/b1v/Spl" [ label="b1v" ]; "Transition to\nambientPass/b1v/Spl" -> "ambientPass" [ label="b1v" ]; "Transition to\nlightingPass/ldv/Spl" [ shape=box ]; "depthPrepass" -> "Transition to\nlightingPass/ldv/Spl" [ label="ldv" ]; "Transition to\nlightingPass/ldv/Spl" -> "lightingPass" [ label="ldv" ]; "Transition to\nlightingPass/d1v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d1v/Spl" [ label="d1v" ]; "Transition to\nlightingPass/d1v/Spl" -> "lightingPass" [ label="d1v" ]; "Transition to\nlightingPass/d2v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d2v/Spl" [ label="d2v" ]; "Transition to\nlightingPass/d2v/Spl" -> "lightingPass" [ label="d2v" ]; "Transition to\nlightingPass/d3v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d3v/Spl" [ label="d3v" ]; "Transition to\nlightingPass/d3v/Spl" -> "lightingPass" [ label="d3v" ]; "Transition to\nlightingPass/d4v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d4v/Spl" [ label="d4v" ]; "Transition to\nlightingPass/d4v/Spl" -> "lightingPass" [ label="d4v" ]; "ssaoRawPass" [ shape=ellipse ]; "Transition to\nssaoBlurPass/rsv/Spl" [ shape=box ]; "ssaoRawPass" -> "Transition to\nssaoBlurPass/rsv/Spl" [ label="rsv" ]; "Transition to\nssaoBlurPass/rsv/Spl" -> "ssaoBlurPass" [ label="rsv" ]; "ssaoMinifyPass3" [ shape=ellipse ]; "Transition to\nssaoRawPass/lp/Spl3" [ shape=box ]; "ssaoMinifyPass3" -> "Transition to\nssaoRawPass/lp/Spl3" [ label="m3v" ]; "Transition to\nssaoRawPass/lp/Spl3" -> "ssaoRawPass" [ label="m3v" ]; "ssaoMinifyPass2" [ shape=ellipse ]; "Transition to\nssaoMinifyPass3/m2v/Spl" [ shape=box ]; "ssaoMinifyPass2" -> "Transition to\nssaoMinifyPass3/m2v/Spl" [ label="m2v" ]; "Transition to\nssaoMinifyPass3/m2v/Spl" -> "ssaoMinifyPass3" [ label="m2v" ]; "ssaoMinifyPass1" [ shape=ellipse ]; "Transition to\nssaoMinifyPass2/m1v/Spl" [ shape=box ]; "ssaoMinifyPass1" -> "Transition to\nssaoMinifyPass2/m1v/Spl" [ label="m1v" ]; "Transition to\nssaoMinifyPass2/m1v/Spl" -> "ssaoMinifyPass2" [ label="m1v" ]; "ssaoLinearisePass" [ shape=ellipse ]; "Transition to\nssaoMinifyPass1/m0v/Spl" [ shape=box ]; "ssaoLinearisePass" -> "Transition to\nssaoMinifyPass1/m0v/Spl" [ label="m0v" ]; "Transition to\nssaoMinifyPass1/m0v/Spl" -> "ssaoMinifyPass1" [ label="m0v" ]; "accumulationPass" [ shape=ellipse ]; "Transition to\ncombinePass/av/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/av/Spl" [ label="av" ]; "Transition to\ncombinePass/av/Spl" -> "combinePass" [ label="av" ]; "Transition to\ncombinePass/rv/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/rv/Spl" [ label="rv" ]; "Transition to\ncombinePass/rv/Spl" -> "combinePass" [ label="rv" ]; } )"; } else { ref = R"(digraph { "depthPrepass" [ shape=ellipse ]; "geometryPass" [ shape=ellipse ]; "Transition to\ngeometryPass/dtv/IRds" [ shape=box ]; "depthPrepass" -> "Transition to\ngeometryPass/dtv/IRds" [ label="dtv" ]; "Transition to\ngeometryPass/dtv/IRds" -> "geometryPass" [ label="dtv" ]; "ambientPass" [ shape=ellipse ]; "finalCombinePass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/ofv/Spl" [ shape=box ]; "ambientPass" -> "Transition to\nfinalCombinePass/ofv/Spl" [ label="ofv" ]; "Transition to\nfinalCombinePass/ofv/Spl" -> "finalCombinePass" [ label="ofv" ]; "lightingPass" [ shape=ellipse ]; "ssaoBlurPass" [ shape=ellipse ]; "Transition to\nambientPass/dfv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/dfv/Spl" [ label="dfv" ]; "Transition to\nambientPass/dfv/Spl" -> "ambientPass" [ label="dfv" ]; "Transition to\nambientPass/spv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/spv/Spl" [ label="spv" ]; "Transition to\nambientPass/spv/Spl" -> "ambientPass" [ label="spv" ]; "Transition to\nambientPass/b1v/Spl" [ shape=box ]; "ssaoBlurPass" -> "Transition to\nambientPass/b1v/Spl" [ label="b1v" ]; "Transition to\nambientPass/b1v/Spl" -> "ambientPass" [ label="b1v" ]; "Transition to\nlightingPass/ldv/Spl" [ shape=box ]; "depthPrepass" -> "Transition to\nlightingPass/ldv/Spl" [ label="ldv" ]; "Transition to\nlightingPass/ldv/Spl" -> "lightingPass" [ label="ldv" ]; "Transition to\nlightingPass/d1v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d1v/Spl" [ label="d1v" ]; "Transition to\nlightingPass/d1v/Spl" -> "lightingPass" [ label="d1v" ]; "Transition to\nlightingPass/d2v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d2v/Spl" [ label="d2v" ]; "Transition to\nlightingPass/d2v/Spl" -> "lightingPass" [ label="d2v" ]; "Transition to\nlightingPass/d3v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d3v/Spl" [ label="d3v" ]; "Transition to\nlightingPass/d3v/Spl" -> "lightingPass" [ label="d3v" ]; "Transition to\nlightingPass/d4v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d4v/Spl" [ label="d4v" ]; "Transition to\nlightingPass/d4v/Spl" -> "lightingPass" [ label="d4v" ]; "ssaoRawPass" [ shape=ellipse ]; "Transition to\nssaoBlurPass/rsv/Spl" [ shape=box ]; "ssaoRawPass" -> "Transition to\nssaoBlurPass/rsv/Spl" [ label="rsv" ]; "Transition to\nssaoBlurPass/rsv/Spl" -> "ssaoBlurPass" [ label="rsv" ]; "ssaoMinifyPass3" [ shape=ellipse ]; "Transition to\nssaoRawPass/lp/Spl3" [ shape=box ]; "ssaoMinifyPass3" -> "Transition to\nssaoRawPass/lp/Spl3" [ label="m3v" ]; "Transition to\nssaoRawPass/lp/Spl3" -> "ssaoRawPass" [ label="m3v" ]; "ssaoMinifyPass2" [ shape=ellipse ]; "Transition to\nssaoMinifyPass3/m2v/Spl" [ shape=box ]; "ssaoMinifyPass2" -> "Transition to\nssaoMinifyPass3/m2v/Spl" [ label="m2v" ]; "Transition to\nssaoMinifyPass3/m2v/Spl" -> "ssaoMinifyPass3" [ label="m2v" ]; "ssaoMinifyPass1" [ shape=ellipse ]; "Transition to\nssaoMinifyPass2/m1v/Spl" [ shape=box ]; "ssaoMinifyPass1" -> "Transition to\nssaoMinifyPass2/m1v/Spl" [ label="m1v" ]; "Transition to\nssaoMinifyPass2/m1v/Spl" -> "ssaoMinifyPass2" [ label="m1v" ]; "ssaoLinearisePass" [ shape=ellipse ]; "Transition to\nssaoMinifyPass1/m0v/Spl" [ shape=box ]; "ssaoLinearisePass" -> "Transition to\nssaoMinifyPass1/m0v/Spl" [ label="m0v" ]; "Transition to\nssaoMinifyPass1/m0v/Spl" -> "ssaoMinifyPass1" [ label="m0v" ]; } )"; } } else if constexpr ( EnableTransparent ) { ref = R"(digraph { "depthPrepass" [ shape=ellipse ]; "geometryPass" [ shape=ellipse ]; "Transition to\ngeometryPass/dtv/IRds" [ shape=box ]; "depthPrepass" -> "Transition to\ngeometryPass/dtv/IRds" [ label="dtv" ]; "Transition to\ngeometryPass/dtv/IRds" -> "geometryPass" [ label="dtv" ]; "ambientPass" [ shape=ellipse ]; "finalCombinePass" [ shape=ellipse ]; "combinePass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/ofv/Spl" [ shape=box ]; "ambientPass" -> "Transition to\nfinalCombinePass/ofv/Spl" [ label="ofv" ]; "Transition to\nfinalCombinePass/ofv/Spl" -> "finalCombinePass" [ label="ofv" ]; "Transition to\nfinalCombinePass/cv/Spl" [ shape=box ]; "combinePass" -> "Transition to\nfinalCombinePass/cv/Spl" [ label="cv" ]; "Transition to\nfinalCombinePass/cv/Spl" -> "finalCombinePass" [ label="cv" ]; "lightingPass" [ shape=ellipse ]; "Transition to\nambientPass/dfv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/dfv/Spl" [ label="dfv" ]; "Transition to\nambientPass/dfv/Spl" -> "ambientPass" [ label="dfv" ]; "Transition to\nambientPass/spv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/spv/Spl" [ label="spv" ]; "Transition to\nambientPass/spv/Spl" -> "ambientPass" [ label="spv" ]; "Transition to\nlightingPass/ldv/Spl" [ shape=box ]; "depthPrepass" -> "Transition to\nlightingPass/ldv/Spl" [ label="ldv" ]; "Transition to\nlightingPass/ldv/Spl" -> "lightingPass" [ label="ldv" ]; "Transition to\nlightingPass/d1v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d1v/Spl" [ label="d1v" ]; "Transition to\nlightingPass/d1v/Spl" -> "lightingPass" [ label="d1v" ]; "Transition to\nlightingPass/d2v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d2v/Spl" [ label="d2v" ]; "Transition to\nlightingPass/d2v/Spl" -> "lightingPass" [ label="d2v" ]; "Transition to\nlightingPass/d3v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d3v/Spl" [ label="d3v" ]; "Transition to\nlightingPass/d3v/Spl" -> "lightingPass" [ label="d3v" ]; "Transition to\nlightingPass/d4v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d4v/Spl" [ label="d4v" ]; "Transition to\nlightingPass/d4v/Spl" -> "lightingPass" [ label="d4v" ]; "accumulationPass" [ shape=ellipse ]; "Transition to\ncombinePass/av/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/av/Spl" [ label="av" ]; "Transition to\ncombinePass/av/Spl" -> "combinePass" [ label="av" ]; "Transition to\ncombinePass/rv/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/rv/Spl" [ label="rv" ]; "Transition to\ncombinePass/rv/Spl" -> "combinePass" [ label="rv" ]; } )"; } else { ref = R"(digraph { "depthPrepass" [ shape=ellipse ]; "geometryPass" [ shape=ellipse ]; "Transition to\ngeometryPass/dtv/IRds" [ shape=box ]; "depthPrepass" -> "Transition to\ngeometryPass/dtv/IRds" [ label="dtv" ]; "Transition to\ngeometryPass/dtv/IRds" -> "geometryPass" [ label="dtv" ]; "ambientPass" [ shape=ellipse ]; "finalCombinePass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/ofv/Spl" [ shape=box ]; "ambientPass" -> "Transition to\nfinalCombinePass/ofv/Spl" [ label="ofv" ]; "Transition to\nfinalCombinePass/ofv/Spl" -> "finalCombinePass" [ label="ofv" ]; "lightingPass" [ shape=ellipse ]; "Transition to\nambientPass/dfv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/dfv/Spl" [ label="dfv" ]; "Transition to\nambientPass/dfv/Spl" -> "ambientPass" [ label="dfv" ]; "Transition to\nambientPass/spv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/spv/Spl" [ label="spv" ]; "Transition to\nambientPass/spv/Spl" -> "ambientPass" [ label="spv" ]; "Transition to\nlightingPass/ldv/Spl" [ shape=box ]; "depthPrepass" -> "Transition to\nlightingPass/ldv/Spl" [ label="ldv" ]; "Transition to\nlightingPass/ldv/Spl" -> "lightingPass" [ label="ldv" ]; "Transition to\nlightingPass/d1v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d1v/Spl" [ label="d1v" ]; "Transition to\nlightingPass/d1v/Spl" -> "lightingPass" [ label="d1v" ]; "Transition to\nlightingPass/d2v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d2v/Spl" [ label="d2v" ]; "Transition to\nlightingPass/d2v/Spl" -> "lightingPass" [ label="d2v" ]; "Transition to\nlightingPass/d3v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d3v/Spl" [ label="d3v" ]; "Transition to\nlightingPass/d3v/Spl" -> "lightingPass" [ label="d3v" ]; "Transition to\nlightingPass/d4v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d4v/Spl" [ label="d4v" ]; "Transition to\nlightingPass/d4v/Spl" -> "lightingPass" [ label="d4v" ]; } )"; } } else if constexpr ( EnableTransparent ) { ref = R"(digraph { "combinePass" [ shape=ellipse ]; "finalCombinePass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/cv/Spl" [ shape=box ]; "combinePass" -> "Transition to\nfinalCombinePass/cv/Spl" [ label="cv" ]; "Transition to\nfinalCombinePass/cv/Spl" -> "finalCombinePass" [ label="cv" ]; "depthPrepass" [ shape=ellipse ]; "accumulationPass" [ shape=ellipse ]; "Transition to\ncombinePass/ldv/Spl" [ shape=box ]; "depthPrepass" -> "Transition to\ncombinePass/ldv/Spl" [ label="ldv" ]; "Transition to\ncombinePass/ldv/Spl" -> "combinePass" [ label="ldv" ]; "Transition to\ncombinePass/av/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/av/Spl" [ label="av" ]; "Transition to\ncombinePass/av/Spl" -> "combinePass" [ label="av" ]; "Transition to\ncombinePass/rv/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/rv/Spl" [ label="rv" ]; "Transition to\ncombinePass/rv/Spl" -> "combinePass" [ label="rv" ]; "Transition to\naccumulationPass/dtv/IRds" [ shape=box ]; "depthPrepass" -> "Transition to\naccumulationPass/dtv/IRds" [ label="dtv" ]; "Transition to\naccumulationPass/dtv/IRds" -> "accumulationPass" [ label="dtv" ]; } )"; } else { ref = R"(digraph { "Transition to\nfinalCombinePass/ldv/Spl" [ shape=box ]; "depthPrepass" [ shape=ellipse ]; "finalCombinePass" [ shape=ellipse ]; "depthPrepass" -> "Transition to\nfinalCombinePass/ldv/Spl" [ label="ldv" ]; "Transition to\nfinalCombinePass/ldv/Spl" -> "finalCombinePass" [ label="ldv" ]; } )"; } } else { if constexpr ( EnableOpaque ) { if constexpr ( EnableSsao ) { if constexpr ( EnableTransparent ) { ref = R"(digraph { "finalCombinePass" [ shape=ellipse ]; "ambientPass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/ofv/Spl" [ shape=box ]; "ambientPass" -> "Transition to\nfinalCombinePass/ofv/Spl" [ label="ofv" ]; "Transition to\nfinalCombinePass/ofv/Spl" -> "finalCombinePass" [ label="ofv" ]; "lightingPass" [ shape=ellipse ]; "Transition to\nambientPass/dfv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/dfv/Spl" [ label="dfv" ]; "Transition to\nambientPass/dfv/Spl" -> "ambientPass" [ label="dfv" ]; "Transition to\nambientPass/spv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/spv/Spl" [ label="spv" ]; "Transition to\nambientPass/spv/Spl" -> "ambientPass" [ label="spv" ]; "geometryPass" [ shape=ellipse ]; "Transition to\nlightingPass/d1v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d1v/Spl" [ label="d1v" ]; "Transition to\nlightingPass/d1v/Spl" -> "lightingPass" [ label="d1v" ]; "Transition to\nlightingPass/d2v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d2v/Spl" [ label="d2v" ]; "Transition to\nlightingPass/d2v/Spl" -> "lightingPass" [ label="d2v" ]; "Transition to\nlightingPass/d3v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d3v/Spl" [ label="d3v" ]; "Transition to\nlightingPass/d3v/Spl" -> "lightingPass" [ label="d3v" ]; "Transition to\nlightingPass/d4v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d4v/Spl" [ label="d4v" ]; "Transition to\nlightingPass/d4v/Spl" -> "lightingPass" [ label="d4v" ]; "Transition to\nlightingPass/ldv/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/ldv/Spl" [ label="ldv" ]; "Transition to\nlightingPass/ldv/Spl" -> "lightingPass" [ label="ldv" ]; "ssaoBlurPass" [ shape=ellipse ]; "Transition to\nambientPass/b1v/Spl" [ shape=box ]; "ssaoBlurPass" -> "Transition to\nambientPass/b1v/Spl" [ label="b1v" ]; "Transition to\nambientPass/b1v/Spl" -> "ambientPass" [ label="b1v" ]; "ssaoRawPass" [ shape=ellipse ]; "Transition to\nssaoBlurPass/rsv/Spl" [ shape=box ]; "ssaoRawPass" -> "Transition to\nssaoBlurPass/rsv/Spl" [ label="rsv" ]; "Transition to\nssaoBlurPass/rsv/Spl" -> "ssaoBlurPass" [ label="rsv" ]; "ssaoMinifyPass3" [ shape=ellipse ]; "Transition to\nssaoRawPass/lp/Spl3" [ shape=box ]; "ssaoMinifyPass3" -> "Transition to\nssaoRawPass/lp/Spl3" [ label="m3v" ]; "Transition to\nssaoRawPass/lp/Spl3" -> "ssaoRawPass" [ label="m3v" ]; "ssaoMinifyPass2" [ shape=ellipse ]; "Transition to\nssaoMinifyPass3/m2v/Spl" [ shape=box ]; "ssaoMinifyPass2" -> "Transition to\nssaoMinifyPass3/m2v/Spl" [ label="m2v" ]; "Transition to\nssaoMinifyPass3/m2v/Spl" -> "ssaoMinifyPass3" [ label="m2v" ]; "ssaoMinifyPass1" [ shape=ellipse ]; "Transition to\nssaoMinifyPass2/m1v/Spl" [ shape=box ]; "ssaoMinifyPass1" -> "Transition to\nssaoMinifyPass2/m1v/Spl" [ label="m1v" ]; "Transition to\nssaoMinifyPass2/m1v/Spl" -> "ssaoMinifyPass2" [ label="m1v" ]; "ssaoLinearisePass" [ shape=ellipse ]; "Transition to\nssaoMinifyPass1/m0v/Spl" [ shape=box ]; "ssaoLinearisePass" -> "Transition to\nssaoMinifyPass1/m0v/Spl" [ label="m0v" ]; "Transition to\nssaoMinifyPass1/m0v/Spl" -> "ssaoMinifyPass1" [ label="m0v" ]; "combinePass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/cv/Spl" [ shape=box ]; "combinePass" -> "Transition to\nfinalCombinePass/cv/Spl" [ label="cv" ]; "Transition to\nfinalCombinePass/cv/Spl" -> "finalCombinePass" [ label="cv" ]; "accumulationPass" [ shape=ellipse ]; "Transition to\ncombinePass/av/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/av/Spl" [ label="av" ]; "Transition to\ncombinePass/av/Spl" -> "combinePass" [ label="av" ]; "Transition to\ncombinePass/rv/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/rv/Spl" [ label="rv" ]; "Transition to\ncombinePass/rv/Spl" -> "combinePass" [ label="rv" ]; "Transition to\naccumulationPass/dtv/IRds" [ shape=box ]; "geometryPass" -> "Transition to\naccumulationPass/dtv/IRds" [ label="dtv" ]; "Transition to\naccumulationPass/dtv/IRds" -> "accumulationPass" [ label="dtv" ]; } )"; } else { ref = R"(digraph { "finalCombinePass" [ shape=ellipse ]; "ambientPass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/ofv/Spl" [ shape=box ]; "ambientPass" -> "Transition to\nfinalCombinePass/ofv/Spl" [ label="ofv" ]; "Transition to\nfinalCombinePass/ofv/Spl" -> "finalCombinePass" [ label="ofv" ]; "lightingPass" [ shape=ellipse ]; "Transition to\nambientPass/dfv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/dfv/Spl" [ label="dfv" ]; "Transition to\nambientPass/dfv/Spl" -> "ambientPass" [ label="dfv" ]; "Transition to\nambientPass/spv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/spv/Spl" [ label="spv" ]; "Transition to\nambientPass/spv/Spl" -> "ambientPass" [ label="spv" ]; "geometryPass" [ shape=ellipse ]; "Transition to\nlightingPass/d1v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d1v/Spl" [ label="d1v" ]; "Transition to\nlightingPass/d1v/Spl" -> "lightingPass" [ label="d1v" ]; "Transition to\nlightingPass/d2v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d2v/Spl" [ label="d2v" ]; "Transition to\nlightingPass/d2v/Spl" -> "lightingPass" [ label="d2v" ]; "Transition to\nlightingPass/d3v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d3v/Spl" [ label="d3v" ]; "Transition to\nlightingPass/d3v/Spl" -> "lightingPass" [ label="d3v" ]; "Transition to\nlightingPass/d4v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d4v/Spl" [ label="d4v" ]; "Transition to\nlightingPass/d4v/Spl" -> "lightingPass" [ label="d4v" ]; "Transition to\nlightingPass/ldv/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/ldv/Spl" [ label="ldv" ]; "Transition to\nlightingPass/ldv/Spl" -> "lightingPass" [ label="ldv" ]; "ssaoBlurPass" [ shape=ellipse ]; "Transition to\nambientPass/b1v/Spl" [ shape=box ]; "ssaoBlurPass" -> "Transition to\nambientPass/b1v/Spl" [ label="b1v" ]; "Transition to\nambientPass/b1v/Spl" -> "ambientPass" [ label="b1v" ]; "ssaoRawPass" [ shape=ellipse ]; "Transition to\nssaoBlurPass/rsv/Spl" [ shape=box ]; "ssaoRawPass" -> "Transition to\nssaoBlurPass/rsv/Spl" [ label="rsv" ]; "Transition to\nssaoBlurPass/rsv/Spl" -> "ssaoBlurPass" [ label="rsv" ]; "ssaoMinifyPass3" [ shape=ellipse ]; "Transition to\nssaoRawPass/lp/Spl3" [ shape=box ]; "ssaoMinifyPass3" -> "Transition to\nssaoRawPass/lp/Spl3" [ label="m3v" ]; "Transition to\nssaoRawPass/lp/Spl3" -> "ssaoRawPass" [ label="m3v" ]; "ssaoMinifyPass2" [ shape=ellipse ]; "Transition to\nssaoMinifyPass3/m2v/Spl" [ shape=box ]; "ssaoMinifyPass2" -> "Transition to\nssaoMinifyPass3/m2v/Spl" [ label="m2v" ]; "Transition to\nssaoMinifyPass3/m2v/Spl" -> "ssaoMinifyPass3" [ label="m2v" ]; "ssaoMinifyPass1" [ shape=ellipse ]; "Transition to\nssaoMinifyPass2/m1v/Spl" [ shape=box ]; "ssaoMinifyPass1" -> "Transition to\nssaoMinifyPass2/m1v/Spl" [ label="m1v" ]; "Transition to\nssaoMinifyPass2/m1v/Spl" -> "ssaoMinifyPass2" [ label="m1v" ]; "ssaoLinearisePass" [ shape=ellipse ]; "Transition to\nssaoMinifyPass1/m0v/Spl" [ shape=box ]; "ssaoLinearisePass" -> "Transition to\nssaoMinifyPass1/m0v/Spl" [ label="m0v" ]; "Transition to\nssaoMinifyPass1/m0v/Spl" -> "ssaoMinifyPass1" [ label="m0v" ]; } )"; } } else if constexpr ( EnableTransparent ) { ref = R"(digraph { "finalCombinePass" [ shape=ellipse ]; "ambientPass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/ofv/Spl" [ shape=box ]; "ambientPass" -> "Transition to\nfinalCombinePass/ofv/Spl" [ label="ofv" ]; "Transition to\nfinalCombinePass/ofv/Spl" -> "finalCombinePass" [ label="ofv" ]; "lightingPass" [ shape=ellipse ]; "Transition to\nambientPass/dfv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/dfv/Spl" [ label="dfv" ]; "Transition to\nambientPass/dfv/Spl" -> "ambientPass" [ label="dfv" ]; "Transition to\nambientPass/spv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/spv/Spl" [ label="spv" ]; "Transition to\nambientPass/spv/Spl" -> "ambientPass" [ label="spv" ]; "geometryPass" [ shape=ellipse ]; "Transition to\nlightingPass/d1v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d1v/Spl" [ label="d1v" ]; "Transition to\nlightingPass/d1v/Spl" -> "lightingPass" [ label="d1v" ]; "Transition to\nlightingPass/d2v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d2v/Spl" [ label="d2v" ]; "Transition to\nlightingPass/d2v/Spl" -> "lightingPass" [ label="d2v" ]; "Transition to\nlightingPass/d3v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d3v/Spl" [ label="d3v" ]; "Transition to\nlightingPass/d3v/Spl" -> "lightingPass" [ label="d3v" ]; "Transition to\nlightingPass/d4v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d4v/Spl" [ label="d4v" ]; "Transition to\nlightingPass/d4v/Spl" -> "lightingPass" [ label="d4v" ]; "Transition to\nlightingPass/ldv/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/ldv/Spl" [ label="ldv" ]; "Transition to\nlightingPass/ldv/Spl" -> "lightingPass" [ label="ldv" ]; "combinePass" [ shape=ellipse ]; "Transition to\nfinalCombinePass/cv/Spl" [ shape=box ]; "combinePass" -> "Transition to\nfinalCombinePass/cv/Spl" [ label="cv" ]; "Transition to\nfinalCombinePass/cv/Spl" -> "finalCombinePass" [ label="cv" ]; "accumulationPass" [ shape=ellipse ]; "Transition to\ncombinePass/av/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/av/Spl" [ label="av" ]; "Transition to\ncombinePass/av/Spl" -> "combinePass" [ label="av" ]; "Transition to\ncombinePass/rv/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/rv/Spl" [ label="rv" ]; "Transition to\ncombinePass/rv/Spl" -> "combinePass" [ label="rv" ]; "Transition to\naccumulationPass/dtv/IRds" [ shape=box ]; "geometryPass" -> "Transition to\naccumulationPass/dtv/IRds" [ label="dtv" ]; "Transition to\naccumulationPass/dtv/IRds" -> "accumulationPass" [ label="dtv" ]; } )"; } else { ref = R"(digraph { "Transition to\nlightingPass/d1v/Spl" [ shape=box ]; "geometryPass" [ shape=ellipse ]; "lightingPass" [ shape=ellipse ]; "geometryPass" -> "Transition to\nlightingPass/d1v/Spl" [ label="d1v" ]; "Transition to\nlightingPass/d1v/Spl" -> "lightingPass" [ label="d1v" ]; "Transition to\nlightingPass/d2v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d2v/Spl" [ label="d2v" ]; "Transition to\nlightingPass/d2v/Spl" -> "lightingPass" [ label="d2v" ]; "Transition to\nlightingPass/d3v/Spl" [ shape=box ]; "geometryPass" -> "Transition to\nlightingPass/d3v/Spl" [ label="d3v" ]; "Transition to\nlightingPass/d3v/Spl" -> "lightingPass" [ label="d3v" ]; "Transition to\nlightingPass/d4v/Spl" [ shape=box ]; "geometryPass" [ shape=ellipse ]; "geometryPass" -> "Transition to\nlightingPass/d4v/Spl" [ label="d4v" ]; "Transition to\nlightingPass/d4v/Spl" -> "lightingPass" [ label="d4v" ]; "Transition to\nlightingPass/ldv/Spl" [ shape=box ]; "geometryPass" [ shape=ellipse ]; "geometryPass" -> "Transition to\nlightingPass/ldv/Spl" [ label="ldv" ]; "Transition to\nlightingPass/ldv/Spl" -> "lightingPass" [ label="ldv" ]; "Transition to\nambientPass/dfv/Spl" [ shape=box ]; "ambientPass" [ shape=ellipse ]; "lightingPass" -> "Transition to\nambientPass/dfv/Spl" [ label="dfv" ]; "Transition to\nambientPass/dfv/Spl" -> "ambientPass" [ label="dfv" ]; "Transition to\nambientPass/spv/Spl" [ shape=box ]; "lightingPass" -> "Transition to\nambientPass/spv/Spl" [ label="spv" ]; "Transition to\nambientPass/spv/Spl" -> "ambientPass" [ label="spv" ]; "Transition to\nfinalCombinePass/ofv/Spl" [ shape=box ]; "finalCombinePass" [ shape=ellipse ]; "ambientPass" -> "Transition to\nfinalCombinePass/ofv/Spl" [ label="ofv" ]; "Transition to\nfinalCombinePass/ofv/Spl" -> "finalCombinePass" [ label="ofv" ]; "geometryPass" [ shape=ellipse ]; } )"; } } else if constexpr ( EnableTransparent ) { ref = R"(digraph { "Transition to\ncombinePass/av/Spl" [ shape=box ]; "accumulationPass" [ shape=ellipse ]; "combinePass" [ shape=ellipse ]; "accumulationPass" -> "Transition to\ncombinePass/av/Spl" [ label="av" ]; "Transition to\ncombinePass/av/Spl" -> "combinePass" [ label="av" ]; "Transition to\ncombinePass/rv/Spl" [ shape=box ]; "accumulationPass" -> "Transition to\ncombinePass/rv/Spl" [ label="rv" ]; "Transition to\ncombinePass/rv/Spl" -> "combinePass" [ label="rv" ]; "Transition to\nfinalCombinePass/cv/Spl" [ shape=box ]; "finalCombinePass" [ shape=ellipse ]; "combinePass" -> "Transition to\nfinalCombinePass/cv/Spl" [ label="cv" ]; "Transition to\nfinalCombinePass/cv/Spl" -> "finalCombinePass" [ label="cv" ]; } )"; } else { ref = R"(digraph { } )"; } } checkEqualSortedLines( stream, ref ) testEnd() } TEST( RenderGraph, VarianceShadowMap ) { testBegin( "testVarianceShadowMap" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & dirGroup = graph.createPassGroup( "Directional" ); auto dirShadowMap = dirGroup.createImage( test::createImage( "dirShadowMap", crg::PixelFormat::eX8_D24_UNORM, 1u, 4u ) ); auto dirVarianceMap = dirGroup.createImage( test::createImage( "dirVarianceMap", crg::PixelFormat::eR32G32_SFLOAT, 1u, 4u ) ); auto buffer = dirGroup.createBuffer( test::createBuffer( "buffer" ) ); auto bufferv = dirGroup.createView( test::createView( "bufferv", buffer ) ); crg::AttachmentArray dirShadows; crg::AttachmentArray dirVariances; { auto intermediate = dirGroup.createImage( test::createImage( "dirIntermediate", crg::PixelFormat::eR32G32_SFLOAT ) ); auto intermediatev = dirGroup.createView( test::createView( "dirIntermediatev", intermediate, crg::PixelFormat::eR32G32_SFLOAT ) ); for ( uint32_t index = 0u; index < 4u; ++index ) { auto shadowMapv = dirGroup.createView( test::createView( "dirShadowMapv" + std::to_string( index ), dirShadowMap, crg::PixelFormat::eX8_D24_UNORM, 0u, 1u, index ) ); auto varianceMapv = dirGroup.createView( test::createView( "dirVarianceMapv" + std::to_string( index ), dirVarianceMap, crg::PixelFormat::eR32G32_SFLOAT, 0u, 1u, index ) ); auto & shadowPass = dirGroup.createPass( "dirShadowPass" + std::to_string( index ) , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto buffera = shadowPass.addClearableOutputStorageBuffer( bufferv, 0u ); auto shadowMapa = shadowPass.addOutputDepthTarget( shadowMapv ); auto varianceMapa = shadowPass.addOutputColourTarget( varianceMapv ); dirShadows.push_back( shadowMapa ); auto & blurPassX = dirGroup.createPass( "dirBlurPassX" + std::to_string( index ) , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , checkSampledIsShaderReadOnly ); } ); blurPassX.addInputStorage( *buffera, 0u ); blurPassX.addInputSampled( *varianceMapa, 1u ); auto intermediatea = blurPassX.addOutputColourTarget( intermediatev ); auto & blurPassY = dirGroup.createPass( "dirBlurPassY" + std::to_string( index ) , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , checkSampledIsShaderReadOnly ); } ); blurPassY.addInputStorage( *buffera, 0u ); blurPassY.addInputSampled( *intermediatea, 1u ); varianceMapa = blurPassY.addOutputColourTarget( varianceMapv ); dirGroup.addGroupOutput( varianceMapv ); dirVariances.push_back( varianceMapa ); } } auto & pntGroup = graph.createPassGroup( "Point" ); auto pntShadowMap = pntGroup.createImage( test::createImage( "pntShadowMap", crg::PixelFormat::eX8_D24_UNORM, 1u, 36u ) ); auto pntVarianceMap = pntGroup.createImage( test::createImage( "pntVarianceMap", crg::PixelFormat::eR32G32_SFLOAT, 1u, 36u ) ); crg::AttachmentArray pntShadows; crg::AttachmentArray pntVariances; { auto intermediate = pntGroup.createImage( test::createImage( "pntIntermediate", crg::PixelFormat::eR32G32_SFLOAT ) ); auto intermediatev = pntGroup.createView( test::createView( "pntIntermediatev", intermediate, crg::PixelFormat::eR32G32_SFLOAT ) ); for ( uint32_t index = 0u; index < 36u; ++index ) { auto shadowMapv = pntGroup.createView( test::createView( "pntShadowMapv" + std::to_string( index ), pntShadowMap, crg::PixelFormat::eX8_D24_UNORM, 0u, 1u, index ) ); auto varianceMapv = pntGroup.createView( test::createView( "pntVarianceMapv" + std::to_string( index ), pntVarianceMap, crg::PixelFormat::eR32G32_SFLOAT, 0u, 1u, index ) ); auto & shadowPass = pntGroup.createPass( "pntShadowPass" + std::to_string( index ) , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto buffera = shadowPass.addClearableOutputStorageBuffer( bufferv, 0u ); auto shadowMapa = shadowPass.addOutputDepthTarget( shadowMapv ); auto varianceMapa = shadowPass.addOutputColourTarget( varianceMapv ); pntShadows.push_back( shadowMapa ); auto & blurPassX = pntGroup.createPass( "pntBlurPassX" + std::to_string( index ) , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , checkSampledIsShaderReadOnly ); } ); blurPassX.addInputStorage( *buffera, 0u ); blurPassX.addInputSampled( *varianceMapa, 1u ); auto intermediatea = blurPassX.addOutputColourTarget( intermediatev ); auto & blurPassY = pntGroup.createPass( "pntBlurPassY" + std::to_string( index ) , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , checkSampledIsShaderReadOnly ); } ); blurPassY.addInputStorage( *buffera, 0u ); blurPassY.addInputSampled( *intermediatea, 1u ); varianceMapa = blurPassY.addOutputColourTarget( varianceMapv ); pntVariances.push_back( varianceMapa ); pntGroup.addGroupOutput( varianceMapv ); } } auto & sptGroup = graph.createPassGroup( "Spot" ); auto sptShadowMap = sptGroup.createImage( test::createImage( "sptShadowMap", crg::PixelFormat::eX8_D24_UNORM, 1u, 10u ) ); auto sptVarianceMap = sptGroup.createImage( test::createImage( "pntVarianceMap", crg::PixelFormat::eR32G32_SFLOAT, 1u, 10u ) ); crg::AttachmentArray sptShadows; crg::AttachmentArray sptVariances; { auto intermediate = sptGroup.createImage( test::createImage( "sptIntermediate", crg::PixelFormat::eR32G32_SFLOAT ) ); auto intermediatev = sptGroup.createView( test::createView( "sptIntermediatev", intermediate, crg::PixelFormat::eR32G32_SFLOAT ) ); for ( uint32_t index = 0u; index < 10u; ++index ) { auto shadowMapv = sptGroup.createView( test::createView( "sptShadowMapv" + std::to_string( index ), sptShadowMap, crg::PixelFormat::eX8_D24_UNORM, 0u, 1u, index ) ); auto varianceMapv = sptGroup.createView( test::createView( "sptVarianceMapv" + std::to_string( index ), sptVarianceMap, crg::PixelFormat::eR32G32_SFLOAT, 0u, 1u, index ) ); auto & shadowPass = sptGroup.createPass( "sptShadowPass" + std::to_string( index ) , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , checkTargetColourIsShaderReadOnly ); } ); auto buffera = shadowPass.addClearableOutputStorageBuffer( bufferv, 0u ); auto shadowMapa = shadowPass.addOutputDepthTarget( shadowMapv ); auto varianceMapa = shadowPass.addOutputColourTarget( varianceMapv ); sptShadows.push_back( shadowMapa ); auto & blurPassX = sptGroup.createPass( "sptBlurPassX" + std::to_string( index ) , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , checkSampledIsShaderReadOnly ); } ); blurPassX.addInputStorage( *buffera, 0u ); blurPassX.addInputSampled( *varianceMapa, 1u ); auto intermediatea = blurPassX.addOutputColourTarget( intermediatev ); auto & blurPassY = sptGroup.createPass( "sptBlurPassY" + std::to_string( index ) , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , checkSampledIsShaderReadOnly ); } ); blurPassY.addInputStorage( *buffera, 0u ); blurPassY.addInputSampled( *intermediatea, 1u ); varianceMapa = blurPassY.addOutputColourTarget( varianceMapv ); sptVariances.push_back( varianceMapa ); sptGroup.addGroupOutput( varianceMapv ); } } auto & objGroup = graph.createPassGroup( "Objects" ); auto depth = graph.createImage( test::createImage( "depth", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto depthv = graph.createView( test::createView( "depthv", depth, crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto & depthPrepass = graph.createPass( "depthPrepass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto deptha = depthPrepass.addOutputDepthTarget ( depthv ); auto colour = graph.createImage( test::createImage( "colour", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto colourv = graph.createView( test::createView( "colourv", colour, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto & backgroundPass = graph.createPass( "backgroundPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); backgroundPass.addInputDepthTarget( *deptha ); auto coloura = backgroundPass.addOutputColourTarget( colourv ); objGroup.addOutput( colourv , crg::makeLayoutState( crg::ImageLayout::eShaderReadOnly ) ); auto & opaquePass = objGroup.createPass( "opaquePass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , checkSampledIsShaderReadOnly ); } ); opaquePass.addInputSampled( *objGroup.mergeAttachments( dirShadows ), 0u ); opaquePass.addInputSampled( *objGroup.mergeAttachments( dirVariances ), 1u ); opaquePass.addInputSampled( *objGroup.mergeAttachments( pntShadows ), 2u ); opaquePass.addInputSampled( *objGroup.mergeAttachments( pntVariances ), 3u ); opaquePass.addInputSampled( *objGroup.mergeAttachments( sptShadows ), 4u ); opaquePass.addInputSampled( *objGroup.mergeAttachments( sptVariances ), 5u ); opaquePass.addInputDepthTarget( *deptha ); coloura = opaquePass.addInOutColourTarget( *coloura ); auto & transparentPass = objGroup.createPass( "transparentPass" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); transparentPass.addInputDepthTarget( *deptha ); transparentPass.addInOutColourTarget( *coloura ); crg::ResourcesCache cache{ handler }; auto & context = getContext(); VkDeviceMemory bufferMemory; cache.createBuffer( context, buffer, bufferMemory ); cache.createBufferView( context, bufferv ); VkDeviceMemory imageMemory; cache.createImage( context, depth, imageMemory ); cache.createImageView( context, depthv ); handler.createBuffer( context, buffer ); handler.createBufferView( context, bufferv ); auto runnable = graph.compile( context ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( RenderGraph, EnvironmentMap ) { testBegin( "testEnvironmentMap" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto depth = graph.createImage( test::createImage( "depth", crg::PixelFormat::eD32_SFLOAT_S8_UINT, 1u, 6u ) ); auto cube = graph.createImage( test::createImageCube( "cube", crg::PixelFormat::eR16G16B16A16_SFLOAT, 8u, 1u ) ); auto cubev = graph.createView( test::createView( "cubev", cube, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0, 8u, 0u, 6u ) ); auto cubes = graph.createImage( test::createImageCube( "cubes", crg::PixelFormat::eR16G16B16A16_SFLOAT, 8u, 6u ) ); auto cubesv = graph.createView( test::createView( "cubesv", cube, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0, 8u, 0u, 36u ) ); auto colour = graph.createImage( test::createImage1D( "colour", crg::PixelFormat::eR16G16B16A16_SFLOAT, 8u, 6u ) ); auto colourv = graph.createView( test::createView( "colourv", colour, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 8u, 0u, 6u ) ); crg::AttachmentArray colourViews; crg::AttachmentArray cubeViews; crg::AttachmentArray cubesViews; for ( auto index = 0u; index < 6u; ++index ) { auto strIndex = std::to_string( index ); auto colourvn = graph.createView( test::createView( "colourv" + strIndex, colour, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, index, 1u ) ); auto cubevn = graph.createView( test::createView( "cubev" + strIndex, cube, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, index, 1u ) ); auto cubesvn = graph.createView( test::createView( "cubesv" + strIndex, cubes, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, index * 6u, 6u ) ); auto depthvn = graph.createView( test::createView( "depthv" + strIndex, depth, crg::PixelFormat::eD32_SFLOAT_S8_UINT, 0u, 1u, index, 1u ) ); auto & opaquePass = graph.createPass( "EnvOpaquePass" + strIndex , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto depthan = opaquePass.addOutputDepthTarget( depthvn ); auto colouran = opaquePass.addOutputColourTarget( colourvn ); auto cubean = opaquePass.addOutputColourTarget( cubevn ); auto cubesan = opaquePass.addOutputColourTarget( cubesvn ); auto & backgroundPass = graph.createPass( "EnvBackgroundPass" + strIndex , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); backgroundPass.addInputDepthTarget( *depthan ); colouran = backgroundPass.addInOutColourTarget( *colouran ); cubean = backgroundPass.addInOutColourTarget( *cubean ); cubesan = backgroundPass.addInOutColourTarget( *cubesan ); auto & transparentPass = graph.createPass( "EnvTransparentPass" + strIndex , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); transparentPass.addInputDepthTarget( *depthan ); colourViews.push_back( transparentPass.addInOutColourTarget( *colouran ) ); cubeViews.push_back( transparentPass.addInOutColourTarget( *cubean ) ); cubesViews.push_back( transparentPass.addInOutColourTarget( *cubesan ) ); } auto & mipsGen = graph.createPass( "EnvMips" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eTransfer ); } ); mipsGen.addInputTransfer( *graph.mergeAttachments( colourViews ) ); mipsGen.addInputTransfer( *graph.mergeAttachments( cubeViews ) ); mipsGen.addInputTransfer( *graph.mergeAttachments( cubesViews ) ); auto attach = mipsGen.addOutputTransferImage( colourv ); mipsGen.addOutputTransferImage( cubev ); mipsGen.addOutputTransferImage( cubesv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); runnable->getDescriptorWriteT( *attach, crg::SamplerDesc{}, 0u, 0u ); testEnd() } TEST( RenderGraph, DisabledPasses ) { testBegin( "testDisabledPasses" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto depth = graph.createImage( test::createImage( "depth", crg::PixelFormat::eD32_SFLOAT_S8_UINT, 1u, 6u ) ); auto cube = graph.createImage( test::createImageCube( "cube", crg::PixelFormat::eR16G16B16A16_SFLOAT, 8u, 1u ) ); auto cubev = graph.createView( test::createView( "cubev", cube, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0, 8u, 0u, 6u ) ); auto cubes = graph.createImage( test::createImageCube( "cubes", crg::PixelFormat::eR16G16B16A16_SFLOAT, 8u, 6u ) ); auto cubesv = graph.createView( test::createView( "cubesv", cube, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0, 8u, 0u, 36u ) ); auto colour = graph.createImage( test::createImage1D( "colour", crg::PixelFormat::eR16G16B16A16_SFLOAT, 8u, 6u ) ); auto colourv = graph.createView( test::createView( "colourv", colour, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 8u, 0u, 6u ) ); crg::AttachmentArray colourViews; crg::AttachmentArray cubeViews; crg::AttachmentArray cubesViews; for ( auto index = 0u; index < 6u; ++index ) { auto strIndex = std::to_string( index ); auto colourvn = graph.createView( test::createView( "colourv" + strIndex, colour, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, index, 1u ) ); auto cubevn = graph.createView( test::createView( "cubev" + strIndex, cube, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, index, 1u ) ); auto cubesvn = graph.createView( test::createView( "cubesv" + strIndex, cubes, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, index * 6u, 6u ) ); auto depthvn = graph.createView( test::createView( "depthv" + strIndex, depth, crg::PixelFormat::eD32_SFLOAT_S8_UINT, 0u, 1u, index, 1u ) ); auto & opaquePass = graph.createPass( "EnvOpaquePass" + strIndex , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader ); } ); auto depthan = opaquePass.addOutputDepthTarget( depthvn ); auto colouran = opaquePass.addOutputColourTarget( colourvn ); auto cubean = opaquePass.addOutputColourTarget( cubevn ); auto cubesan = opaquePass.addOutputColourTarget( cubesvn ); auto & backgroundPass = graph.createPass( "EnvBackgroundPass" + strIndex , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , test::checkDummy, 0u, false ); } ); backgroundPass.addInputDepthTarget( *depthan ); colouran = backgroundPass.addInOutColourTarget( *colouran ); cubean = backgroundPass.addInOutColourTarget( *cubean ); cubesan = backgroundPass.addInOutColourTarget( *cubesan ); auto & transparentPass = graph.createPass( "EnvTransparentPass" + strIndex , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eFragmentShader , test::checkDummy, 0u, false ); } ); transparentPass.addInputDepthTarget( *depthan ); colourViews.push_back( transparentPass.addInOutColourTarget( *colouran ) ); cubeViews.push_back( transparentPass.addInOutColourTarget( *cubean ) ); cubesViews.push_back( transparentPass.addInOutColourTarget( *cubesan ) ); } auto & mipsGen = graph.createPass( "EnvMips" , [&testCounts]( crg::FramePass const & framePass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return createDummy( testCounts , framePass, context, runGraph, crg::PipelineStageFlags::eTransfer ); } ); mipsGen.addInputTransfer( *graph.mergeAttachments( colourViews ) ); mipsGen.addInputTransfer( *graph.mergeAttachments( cubeViews ) ); mipsGen.addInputTransfer( *graph.mergeAttachments( cubesViews ) ); mipsGen.addOutputTransferImage( colourv ); mipsGen.addOutputTransferImage( cubev ); mipsGen.addOutputTransferImage( cubesv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } testSuiteMain() ================================================ FILE: test/TestRenderPass.cpp ================================================ #include "Common.hpp" #include #include #include #include #include #include #include namespace { TEST( FramePass, Log ) { testBegin( "testLog" ) auto callback = []( std::string_view msg, bool newLine )noexcept { if ( newLine ) fmt::print( "{} Callback\n", msg.data() ); else fmt::print( "{} Callback ", msg.data() ); }; crg::Logger::setTraceCallback( callback ); crg::Logger::setDebugCallback( callback ); crg::Logger::setInfoCallback( callback ); crg::Logger::setWarningCallback( callback ); crg::Logger::setErrorCallback( callback ); crg::Logger::logTrace( "traceCoinNoNL ", false ); crg::Logger::logTrace( "traceCoinNL" ); crg::Logger::logDebug( "debugCoinNoNL ", false ); crg::Logger::logDebug( "debugCoinNL" ); crg::Logger::logInfo( "infoCoinNoNL ", false ); crg::Logger::logInfo( "infoCoinNL" ); crg::Logger::logWarning( "warningCoinNoNL ", false ); crg::Logger::logWarning( "warningCoinNL" ); crg::Logger::logError( "errorCoinNoNL ", false ); crg::Logger::logError( "errorCoinNL" ); crg::Logger::setTraceCallback( nullptr ); crg::Logger::setDebugCallback( nullptr ); crg::Logger::setInfoCallback( nullptr ); crg::Logger::setWarningCallback( nullptr ); crg::Logger::setErrorCallback( nullptr ); crg::Logger::logTrace( "No callback traceCoinNoNL ", false ); crg::Logger::logTrace( "No callback traceCoinNL" ); crg::Logger::logDebug( "No callback debugCoinNoNL ", false ); crg::Logger::logDebug( "No callback debugCoinNL" ); crg::Logger::logInfo( "No callback infoCoinNoNL ", false ); crg::Logger::logInfo( "No callback infoCoinNL" ); crg::Logger::logWarning( "No callback warningCoinNoNL ", false ); crg::Logger::logWarning( "No callback warningCoinNL" ); crg::Logger::logError( "No callback errorCoinNoNL ", false ); crg::Logger::logError( "No callback errorCoinNL" ); testEnd() } TEST( FramePass, RenderPass_1C ) { testBegin( "testRenderPass_1C" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "1C", crg::RunnablePassCreator{} ); auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv = graph.createView( test::createView( "rtv", rt ) ); pass.addOutputColourTarget( rtv ); check( pass.getName() == "1C" ) check( pass.getTargets().size() == 1u ) check( pass.getTargets()[0]->view() == rtv ) testEnd() } TEST( FramePass, RenderPass_2C ) { testBegin( "testRenderPass_2C" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "2C", crg::RunnablePassCreator{} ); auto rt1 = graph.createImage( test::createImage( "rt1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv1 = graph.createView( test::createView( "rtv1", rt1 ) ); pass.addOutputColourTarget( rtv1 ); auto rt2 = graph.createImage( test::createImage( "rt2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv2 = graph.createView( test::createView( "rtv2", rt2 ) ); pass.addOutputColourTarget( rtv2 ); check( pass.getName() == "2C" ) check( pass.getTargets().size() == 2u ) check( pass.getTargets()[0]->view() == rtv1 ) check( pass.getTargets()[1]->view() == rtv2 ) testEnd() } TEST( FramePass, RenderPass_0C_1I ) { testBegin( "testRenderPass_0C_1I" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "0C_1I", crg::RunnablePassCreator{} ); auto in = graph.createImage( test::createImage( "in", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv = graph.createView( test::createView( "inv", in ) ); auto attach = crg::Attachment::createDefault( inv ); pass.addInputSampled( attach, 1u ); check( pass.getName() == "0C_1I" ) check( pass.getSampled().size() == 1u ) check( pass.getSampled().begin()->second.attach->view() == inv ) testEnd() } TEST( FramePass, RenderPass_0C_2I ) { testBegin( "testRenderPass_0C_2I" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "0C_2I", crg::RunnablePassCreator{} ); auto in1 = graph.createImage( test::createImage( "in1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv1 = graph.createView( test::createView( "inv1", in1 ) ); auto attach1 = crg::Attachment::createDefault( inv1 ); pass.addInputSampled( attach1, 1u ); auto in2 = graph.createImage( test::createImage( "in2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv2 = graph.createView( test::createView( "inv2", in2 ) ); auto attach2 = crg::Attachment::createDefault( inv2 ); pass.addInputSampled( attach2, 2u ); check( pass.getName() == "0C_2I" ) check( pass.getSampled().size() == 2u ) check( pass.getSampled().begin()->second.attach->view() == inv1 ) check( pass.getSampled().rbegin()->second.attach->view() == inv2 ) testEnd() } TEST( FramePass, RenderPass_1C_1I ) { testBegin( "testRenderPass_1C_1I" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "1C_1I", crg::RunnablePassCreator{} ); auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv = graph.createView( test::createView( "rtv", rt ) ); pass.addOutputColourTarget( rtv ); auto in = graph.createImage( test::createImage( "in", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv = graph.createView( test::createView( "inv", in ) ); auto attach = crg::Attachment::createDefault( inv ); pass.addInputSampled( attach, 1u ); check( pass.getName() == "1C_1I" ) check( pass.getTargets().size() == 1u ) check( pass.getTargets()[0]->view() == rtv ) check( pass.getSampled().size() == 1u ) check( pass.getSampled().begin()->second.attach->view() == inv ) testEnd() } TEST( FramePass, RenderPass_1C_2I ) { testBegin( "testRenderPass_1C_2I" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "1C_2I", crg::RunnablePassCreator{} ); auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv = graph.createView( test::createView( "rtv", rt ) ); pass.addOutputColourTarget( rtv ); auto in1 = graph.createImage( test::createImage( "in1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv1 = graph.createView( test::createView( "inv1", in1 ) ); auto attach1 = crg::Attachment::createDefault( inv1 ); pass.addInputSampled( attach1, 1u ); auto in2 = graph.createImage( test::createImage( "in2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv2 = graph.createView( test::createView( "inv2", in2 ) ); auto attach2 = crg::Attachment::createDefault( inv2 ); pass.addInputSampled( attach2, 2u ); check( pass.getName() == "1C_2I" ) check( pass.getTargets().size() == 1u ) check( pass.getTargets()[0]->view() == rtv ) check( pass.getSampled().size() == 2u ) check( pass.getSampled().begin()->second.attach->view() == inv1 ) check( pass.getSampled().rbegin()->second.attach->view() == inv2 ) testEnd() } TEST( FramePass, RenderPass_2C_1I ) { testBegin( "testRenderPass_2C_1I" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "2C_1I", crg::RunnablePassCreator{} ); auto rt1 = graph.createImage( test::createImage( "rt1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv1 = graph.createView( test::createView( "rtv1", rt1 ) ); pass.addOutputColourTarget( rtv1 ); auto rt2 = graph.createImage( test::createImage( "rt2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv2 = graph.createView( test::createView( "rtv2", rt2 ) ); pass.addOutputColourTarget( rtv2 ); auto in = graph.createImage( test::createImage( "in", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv = graph.createView( test::createView( "inv", in ) ); auto attach = crg::Attachment::createDefault( inv ); pass.addInputSampled( attach, 1u ); check( pass.getName() == "2C_1I" ) check( pass.getTargets().size() == 2u ) check( pass.getTargets()[0]->view() == rtv1 ) check( pass.getTargets()[1]->view() == rtv2 ) check( pass.getSampled().size() == 1u ) check( pass.getSampled().begin()->second.attach->view() == inv ) testEnd() } TEST( FramePass, RenderPass_2C_2I ) { testBegin( "testRenderPass_2C_2I" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "2C_2I", crg::RunnablePassCreator{} ); auto rt1 = graph.createImage( test::createImage( "rt1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv1 = graph.createView( test::createView( "rtv1", rt1 ) ); pass.addOutputColourTarget( rtv1 ); auto rt2 = graph.createImage( test::createImage( "rt2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv2 = graph.createView( test::createView( "rtv2", rt2 ) ); pass.addOutputColourTarget( rtv2 ); auto in1 = graph.createImage( test::createImage( "in1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv1 = graph.createView( test::createView( "inv1", in1 ) ); auto attach1 = crg::Attachment::createDefault( inv1 ); pass.addInputSampled( attach1, 1u ); auto in2 = graph.createImage( test::createImage( "in2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv2 = graph.createView( test::createView( "inv2", in2 ) ); auto attach2 = crg::Attachment::createDefault( inv2 ); pass.addInputSampled( attach2, 2u ); check( pass.getName() == "2C_2I" ) check( pass.getTargets().size() == 2u ) check( pass.getTargets()[0]->view() == rtv1 ) check( pass.getTargets()[1]->view() == rtv2 ) check( pass.getSampled().size() == 2u ) check( pass.getSampled().begin()->second.attach->view() == inv1 ) check( pass.getSampled().rbegin()->second.attach->view() == inv2 ) testEnd() } TEST( FramePass, RenderPass_0C_DS ) { testBegin( "testRenderPass_0C_DS" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "0C_DS", crg::RunnablePassCreator{} ); auto ds = graph.createImage( test::createImage( "ds", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto dsv = graph.createView( test::createView( "dsv", ds ) ); pass.addOutputDepthStencilTarget( dsv ); check( pass.getName() == "0C_DS" ) check( pass.getTargets().size() == 1u ) check( pass.getTargets()[0]->view() == dsv ) testEnd() } TEST( FramePass, RenderPass_1C_DS ) { testBegin( "testRenderPass_1C_DS" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "1C_DS", crg::RunnablePassCreator{} ); auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv = graph.createView( test::createView( "rtv", rt ) ); pass.addOutputColourTarget( rtv ); auto ds = graph.createImage( test::createImage( "ds", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto dsv = graph.createView( test::createView( "dsv", ds ) ); pass.addOutputDepthStencilTarget( dsv ); check( pass.getName() == "1C_DS" ) check( pass.getTargets().size() == 2u ) check( pass.getTargets()[0]->view() == dsv ) check( pass.getTargets()[1]->view() == rtv ) testEnd() } TEST( FramePass, RenderPass_2C_DS ) { testBegin( "testRenderPass_2C_DS" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "2C_DS", crg::RunnablePassCreator{} ); auto rt1 = graph.createImage( test::createImage( "rt1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv1 = graph.createView( test::createView( "rtv1", rt1 ) ); pass.addOutputColourTarget( rtv1 ); auto rt2 = graph.createImage( test::createImage( "rt2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv2 = graph.createView( test::createView( "rtv2", rt2 ) ); pass.addOutputColourTarget( rtv2 ); auto ds = graph.createImage( test::createImage( "ds", crg::PixelFormat::eD32_SFLOAT ) ); auto dsv = graph.createView( test::createView( "dsv", ds ) ); pass.addOutputDepthStencilTarget( dsv ); check( pass.getName() == "2C_DS" ) check( pass.getTargets().size() == 3u ) check( pass.getTargets()[0]->view() == dsv ) check( pass.getTargets()[1]->view() == rtv1 ) check( pass.getTargets()[2]->view() == rtv2 ) testEnd() } TEST( FramePass, RenderPass_0C_1I_DS ) { testBegin( "testRenderPass_0C_1I_DS" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "0C_1I_DS", crg::RunnablePassCreator{} ); auto in = graph.createImage( test::createImage( "in", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv = graph.createView( test::createView( "inv", in ) ); auto attach = crg::Attachment::createDefault( inv ); pass.addInputSampled( attach, 1u ); auto ds = graph.createImage( test::createImage( "ds", crg::PixelFormat::eD32_SFLOAT ) ); auto dsv = graph.createView( test::createView( "dsv", ds ) ); pass.addOutputDepthStencilTarget( dsv ); check( pass.getName() == "0C_1I_DS" ) check( pass.getTargets().size() == 1u ) check( pass.getTargets()[0]->view() == dsv ) check( pass.getSampled().size() == 1u ) check( pass.getSampled().begin()->second.attach->view() == inv ) testEnd() } TEST( FramePass, RenderPass_0C_2I_DS ) { testBegin( "testRenderPass_0C_2I_DS" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "0C_2I_DS", crg::RunnablePassCreator{} ); auto in1 = graph.createImage( test::createImage( "in1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv1 = graph.createView( test::createView( "inv1", in1 ) ); auto attach1 = crg::Attachment::createDefault( inv1 ); pass.addInputSampled( attach1, 1u ); auto in2 = graph.createImage( test::createImage( "in2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv2 = graph.createView( test::createView( "inv2", in2 ) ); auto attach2 = crg::Attachment::createDefault( inv2 ); pass.addInputSampled( attach2, 2u ); auto ds = graph.createImage( test::createImage( "ds", crg::PixelFormat::eD32_SFLOAT ) ); auto dsv = graph.createView( test::createView( "dsv", ds ) ); pass.addOutputDepthStencilTarget( dsv ); check( pass.getName() == "0C_2I_DS" ) check( pass.getTargets().size() == 1u ) check( pass.getTargets()[0]->view() == dsv ) check( pass.getSampled().size() == 2u ) check( pass.getSampled().begin()->second.attach->view() == inv1 ) check( pass.getSampled().rbegin()->second.attach->view() == inv2 ) testEnd() } TEST( FramePass, RenderPass_1C_1I_DS ) { testBegin( "testRenderPass_1C_1I_DS" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "1C_1I_DS", crg::RunnablePassCreator{} ); auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv = graph.createView( test::createView( "rtv", rt ) ); pass.addOutputColourTarget( rtv ); auto in = graph.createImage( test::createImage( "in", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv = graph.createView( test::createView( "inv", in ) ); auto attach = crg::Attachment::createDefault( inv ); pass.addInputSampled( attach, 1u ); auto ds = graph.createImage( test::createImage( "ds", crg::PixelFormat::eD32_SFLOAT ) ); auto dsv = graph.createView( test::createView( "dsv", ds ) ); pass.addOutputDepthStencilTarget( dsv ); check( pass.getName() == "1C_1I_DS" ) check( pass.getTargets().size() == 2u ) check( pass.getTargets()[0]->view() == dsv ) check( pass.getTargets()[1]->view() == rtv ) check( pass.getSampled().size() == 1u ) check( pass.getSampled().begin()->second.attach->view() == inv ) testEnd() } TEST( FramePass, RenderPass_1C_2I_DS ) { testBegin( "testRenderPass_1C_2I_DS" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "1C_2I_DS", crg::RunnablePassCreator{} ); auto rt = graph.createImage( test::createImage( "rt", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv = graph.createView( test::createView( "rtv", rt ) ); pass.addOutputColourTarget( rtv ); auto in1 = graph.createImage( test::createImage( "in1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv1 = graph.createView( test::createView( "inv1", in1 ) ); auto attach1 = crg::Attachment::createDefault( inv1 ); pass.addInputSampled( attach1, 1u ); auto in2 = graph.createImage( test::createImage( "in2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv2 = graph.createView( test::createView( "inv2", in2 ) ); auto attach2 = crg::Attachment::createDefault( inv2 ); pass.addInputSampled( attach2, 2u ); auto ds = graph.createImage( test::createImage( "ds", crg::PixelFormat::eD32_SFLOAT ) ); auto dsv = graph.createView( test::createView( "dsv", ds ) ); pass.addOutputDepthStencilTarget( dsv ); check( pass.getName() == "1C_2I_DS" ) check( pass.getTargets().size() == 2u ) check( pass.getTargets()[0]->view() == dsv ) check( pass.getTargets()[1]->view() == rtv ) check( pass.getSampled().size() == 2u ) check( pass.getSampled().begin()->second.attach->view() == inv1 ) check( pass.getSampled().rbegin()->second.attach->view() == inv2 ) testEnd() } TEST( FramePass, RenderPass_2C_1I_DS ) { testBegin( "testRenderPass_2C_1I_DS" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "2C_1I_DS", crg::RunnablePassCreator{} ); auto rt1 = graph.createImage( test::createImage( "rt1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv1 = graph.createView( test::createView( "rtv1", rt1 ) ); pass.addOutputColourTarget( rtv1 ); auto rt2 = graph.createImage( test::createImage( "rt2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv2 = graph.createView( test::createView( "rtv2", rt2 ) ); pass.addOutputColourTarget( rtv2 ); auto in = graph.createImage( test::createImage( "in", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv = graph.createView( test::createView( "inv", in ) ); auto attach = crg::Attachment::createDefault( inv ); pass.addInputSampled( attach, 1u ); auto ds = graph.createImage( test::createImage( "ds", crg::PixelFormat::eD32_SFLOAT ) ); auto dsv = graph.createView( test::createView( "dsv", ds ) ); pass.addOutputDepthStencilTarget( dsv ); check( pass.getName() == "2C_1I_DS" ) check( pass.getTargets().size() == 3u ) check( pass.getTargets()[0]->view() == dsv ) check( pass.getTargets()[1]->view() == rtv1 ) check( pass.getTargets()[2]->view() == rtv2 ) check( pass.getSampled().size() == 1u ) check( pass.getSampled().begin()->second.attach->view() == inv ) testEnd() } TEST( FramePass, RenderPass_2C_2I_DS ) { testBegin( "testRenderPass_2C_2I_DS" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & pass = graph.createPass( "2C_2I_DS", crg::RunnablePassCreator{} ); auto rt1 = graph.createImage( test::createImage( "rt1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv1 = graph.createView( test::createView( "rtv1", rt1 ) ); pass.addOutputColourTarget( rtv1 ); auto rt2 = graph.createImage( test::createImage( "rt2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto rtv2 = graph.createView( test::createView( "rtv2", rt2 ) ); pass.addOutputColourTarget( rtv2 ); auto in1 = graph.createImage( test::createImage( "in1", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv1 = graph.createView( test::createView( "inv1", in1 ) ); auto attach1 = crg::Attachment::createDefault( inv1 ); pass.addInputSampled( attach1, 1u ); auto in2 = graph.createImage( test::createImage( "in2", crg::PixelFormat::eR32G32B32A32_SFLOAT ) ); auto inv2 = graph.createView( test::createView( "inv2", in2 ) ); auto attach2 = crg::Attachment::createDefault( inv2 ); pass.addInputSampled( attach2, 2u ); auto ds = graph.createImage( test::createImage( "ds", crg::PixelFormat::eD32_SFLOAT ) ); auto dsv = graph.createView( test::createView( "dsv", ds ) ); pass.addOutputDepthStencilTarget( dsv ); check( pass.getName() == "2C_2I_DS" ) check( pass.getTargets().size() == 3u ) check( pass.getTargets()[0]->view() == dsv ) check( pass.getTargets()[1]->view() == rtv1 ) check( pass.getTargets()[2]->view() == rtv2 ) check( pass.getSampled().size() == 2u ) check( pass.getSampled().begin()->second.attach->view() == inv1 ) check( pass.getSampled().rbegin()->second.attach->view() == inv2 ) testEnd() } } testSuiteMain() ================================================ FILE: test/TestRunnablePass.cpp ================================================ #include "Common.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { crg::GraphContext & getContext() { return test::getDummyContext(); } TEST( RunnablePass, BufferCopy_I_O ) { testBegin( "testBufferCopy_I_O" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer1 = graph.createBuffer( test::createBuffer( "buffer1" ) ); auto buffer2 = graph.createBuffer( test::createBuffer( "buffer2" ) ); auto buffer1v = graph.createView( test::createView( "buffer1v", buffer1 ) ); auto buffer2v = graph.createView( test::createView( "buffer2v", buffer2 ) ); auto buffer1a = crg::Attachment::createDefault( buffer1v ); auto & testPass = graph.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::BufferCopy >( pass, context, runGraph , 0u, 1024u ); } ); testPass.addInputTransfer( buffer1a ); testPass.addOutputTransferBuffer( buffer2v ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, BufferCopy_IO_IO ) { testBegin( "testBufferCopy_IO_IO" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer1 = graph.createBuffer( test::createBuffer( "buffer1" ) ); auto buffer2 = graph.createBuffer( test::createBuffer( "buffer2" ) ); auto buffer1v = graph.createView( test::createView( "buffer1v", buffer1 ) ); auto buffer2v = graph.createView( test::createView( "buffer2v", buffer2 ) ); auto buffer1a = crg::Attachment::createDefault( buffer1v ); auto buffer2a = crg::Attachment::createDefault( buffer2v ); auto & testPass = graph.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::BufferCopy >( pass, context, runGraph , 0u, 512u ); } ); testPass.addInOutTransfer( buffer1a ); testPass.addInOutTransfer( buffer2a ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, BufferToImageCopy ) { testBegin( "testBufferToImageCopy" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto buffer = graph.createBuffer( test::createBuffer( "buffer" ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto bufferv = graph.createView( test::createView( "bufferv", buffer ) ); auto buffera = crg::Attachment::createDefault( bufferv ); auto & testPass = graph.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::BufferToImageCopy >( pass, context, runGraph , crg::Offset3D{}, crg::Extent3D{ 1024, 1024, 1u } ); } ); testPass.addInputTransfer( buffera ); testPass.addOutputTransferImage( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( RunnablePass, GenerateMipmaps ) { testBegin( "testGenerateMipmaps" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT, 10u ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 10u, 0u, 1u ) ); auto resulta = crg::Attachment::createDefault( resultv ); auto & testPass = graph.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::GenerateMipmaps >( pass, context, runGraph ); } ); testPass.addInOutTransfer( resulta ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( RunnablePass, ImageBlit ) { testBegin( "testImageBlit" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input = graph.createImage( test::createImage( "input", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto inputv = graph.createView( test::createView( "inputv", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto inputa = crg::Attachment::createDefault( inputv ); auto & testPass = graph.createPass( "Pass" , [inputv, resultv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageBlit >( pass, context, runGraph , crg::Rect3D{ crg::Offset3D{}, getExtent( inputv ) } , crg::Rect3D{ crg::Offset3D{}, getExtent( resultv ) } , crg::FilterMode::eLinear ); } ); testPass.addInputTransfer( inputa ); testPass.addOutputTransferImage( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( RunnablePass, Image3DBlit ) { testBegin( "testImage3DBlit" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input = graph.createImage( test::createImage3D( "input", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto result = graph.createImage( test::createImage3D( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto inputv = graph.createView( test::createView( "inputv", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto inputa = crg::Attachment::createDefault( inputv ); auto & testPass = graph.createPass( "Pass" , [inputv, resultv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageBlit >( pass, context, runGraph , crg::Rect3D{ crg::Offset3D{}, getExtent( inputv ) } , crg::Rect3D{ crg::Offset3D{}, getExtent( resultv ) } , crg::FilterMode::eLinear ); } ); testPass.addInputTransfer( inputa ); testPass.addOutputTransferImage( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( RunnablePass, ImageCopy_Base ) { testBegin( "testImageCopy_Base" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input = graph.createImage( test::createImage( "input", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto inputv = graph.createView( test::createView( "inputv", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto inputa = crg::Attachment::createDefault( inputv ); auto & testPass = graph.createPass( "Pass" , [inputv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageCopy >( pass, context, runGraph , getExtent( inputv ) ); } ); testPass.addInputTransfer( inputa ); testPass.addOutputTransferImage( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, ImageCopy_SingleInputMultipleOutputsSameImage ) { testBegin( "ImageCopy_SingleInputMultipleOutputsSameImage" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input = graph.createImage( test::createImage( "input", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 6u ) ); auto inputv = graph.createView( test::createView( "inputv", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto resultv0 = graph.createView( test::createView( "result0v", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto resultv1 = graph.createView( test::createView( "result1v", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 1u, 1u ) ); auto resultv2 = graph.createView( test::createView( "result2v", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 2u, 1u ) ); auto resultv3 = graph.createView( test::createView( "result3v", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 3u, 1u ) ); auto resultv4 = graph.createView( test::createView( "result4v", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 4u, 1u ) ); auto resultv5 = graph.createView( test::createView( "result5v", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 5u, 1u ) ); auto inputa = crg::Attachment::createDefault( inputv ); auto & testPass = graph.createPass( "Pass" , [inputv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageCopy >( pass, context, runGraph , getExtent( inputv ), crg::ImageLayout::eShaderReadOnly ); } ); testPass.addInputTransfer( inputa ); testPass.addOutputTransferImage( resultv0 ); testPass.addOutputTransferImage( resultv1 ); testPass.addOutputTransferImage( resultv2 ); testPass.addOutputTransferImage( resultv3 ); testPass.addOutputTransferImage( resultv4 ); testPass.addOutputTransferImage( resultv5 ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, ImageCopy_SingleInputMultipleOutputsDifferentImage ) { testBegin( "ImageCopy_SingleInputMultipleOutputsDifferentImage" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input = graph.createImage( test::createImage( "input", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto result0 = graph.createImage( test::createImage( "result0", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 3u ) ); auto result1 = graph.createImage( test::createImage( "result1", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 3u ) ); auto inputv = graph.createView( test::createView( "inputv", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto result0v0 = graph.createView( test::createView( "result0v", result0, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto result0v1 = graph.createView( test::createView( "result1v", result0, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 1u, 1u ) ); auto result0v2 = graph.createView( test::createView( "result2v", result0, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 2u, 1u ) ); auto result1v0 = graph.createView( test::createView( "result3v", result1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto result1v1 = graph.createView( test::createView( "result4v", result1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 1u, 1u ) ); auto result1v2 = graph.createView( test::createView( "result5v", result1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 2u, 1u ) ); auto inputa = crg::Attachment::createDefault( inputv ); auto & testPass = graph.createPass( "Pass" , [inputv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageCopy >( pass, context, runGraph , getExtent( inputv ) ); } ); testPass.addInputTransfer( inputa ); testPass.addOutputTransferImage( result0v0 ); testPass.addOutputTransferImage( result0v1 ); testPass.addOutputTransferImage( result0v2 ); testPass.addOutputTransferImage( result1v0 ); testPass.addOutputTransferImage( result1v1 ); testPass.addOutputTransferImage( result1v2 ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, ImageCopy_MultipleInputsSameImageSingleOutput ) { testBegin( "ImageCopy_SingleInputMultipleOutputsSameImage" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input = graph.createImage( test::createImage( "input", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 6u ) ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto inputv0 = graph.createView( test::createView( "inputv0", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto inputv1 = graph.createView( test::createView( "inputv1", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 1u, 1u ) ); auto inputv2 = graph.createView( test::createView( "inputv2", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 2u, 1u ) ); auto inputv3 = graph.createView( test::createView( "inputv3", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 3u, 1u ) ); auto inputv4 = graph.createView( test::createView( "inputv4", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 4u, 1u ) ); auto inputv5 = graph.createView( test::createView( "inputv5", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 5u, 1u ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto & testPass = graph.createPass( "Pass" , [inputv0]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageCopy >( pass, context, runGraph , getExtent( inputv0 ), crg::ImageLayout::eShaderReadOnly ); } ); testPass.addInputTransferImage( inputv0 ); testPass.addInputTransferImage( inputv1 ); testPass.addInputTransferImage( inputv2 ); testPass.addInputTransferImage( inputv3 ); testPass.addInputTransferImage( inputv4 ); testPass.addInputTransferImage( inputv5 ); testPass.addOutputTransferImage( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, ImageCopy_MultipleInputsDifferentImageSingleOutput ) { testBegin( "ImageCopy_MultipleInputsDifferentImageSingleOutput" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input0 = graph.createImage( test::createImage( "input0", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 3u ) ); auto input1 = graph.createImage( test::createImage( "input1", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 3u ) ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto input0v0 = graph.createView( test::createView( "input0v0", input0, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto input0v1 = graph.createView( test::createView( "input0v1", input0, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 1u, 1u ) ); auto input0v2 = graph.createView( test::createView( "input0v2", input0, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 2u, 1u ) ); auto input1v0 = graph.createView( test::createView( "input1v0", input1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto input1v1 = graph.createView( test::createView( "input1v1", input1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 1u, 1u ) ); auto input1v2 = graph.createView( test::createView( "input1v2", input1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 2u, 1u ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto & testPass = graph.createPass( "Pass" , [result]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageCopy >( pass, context, runGraph , getExtent( result ) ); } ); testPass.addInputTransferImage( input0v0 ); testPass.addInputTransferImage( input0v1 ); testPass.addInputTransferImage( input0v2 ); testPass.addInputTransferImage( input1v0 ); testPass.addInputTransferImage( input1v1 ); testPass.addInputTransferImage( input1v2 ); testPass.addOutputTransferImage( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, ImageCopy_OutputLayout ) { testBegin( "testImageCopy_OutputLayout" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input = graph.createImage( test::createImage( "input", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto inputv = graph.createView( test::createView( "inputv", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto inputa = crg::Attachment::createDefault( inputv ); auto & testPass = graph.createPass( "Pass" , [inputv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageCopy >( pass, context, runGraph , getExtent( inputv ) , crg::ImageLayout::eShaderReadOnly ); } ); testPass.addInputTransfer( inputa ); testPass.addOutputTransferImage( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, ImageCopy_BackAndForth ) { testBegin( "testImageCopy_BackAndForth" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input = graph.createImage( test::createImage( "input", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto inputv = graph.createView( test::createView( "inputv", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto inputa = crg::Attachment::createDefault( inputv ); auto resulta = crg::Attachment::createDefault( resultv ); auto & testPass = graph.createPass( "Pass" , [inputv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageCopy >( pass, context, runGraph , getExtent( inputv ) , crg::ImageLayout::eShaderReadOnly ); } ); testPass.addInputTransfer( inputa ); testPass.addOutputTransferImage( resultv ); testPass.addInputTransfer( resulta ); testPass.addOutputTransferImage( inputv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, ImageToBufferCopy ) { testBegin( "testImageToBufferCopy" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto input = graph.createImage( test::createImage( "input", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto buffer = graph.createBuffer( test::createBuffer( "buffer" ) ); auto inputv = graph.createView( test::createView( "inputv", input, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto bufferv = graph.createView( test::createView( "bufferv", buffer ) ); auto inputa = crg::Attachment::createDefault( inputv ); auto & testPass = graph.createPass( "Pass" , [inputv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::ImageToBufferCopy >( pass, context, runGraph , crg::Offset3D{}, getExtent( inputv ) ); } ); testPass.addInputTransfer( inputa ); testPass.addOutputTransferBuffer( bufferv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( RunnablePass, ComputePass ) { testBegin( "testComputePass" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, "/" + testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "buffer" ) ); auto bufferv = graph.createView( test::createView( "bufferv", buffer ) ); auto indirect = graph.createBuffer( test::createBuffer( "indirect" ) ); auto indirectv = graph.createView( test::createView( "indirectv", indirect ) ); auto & testPass1 = graph.createPass( "Pass1" , [&indirectv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::cp::Config cfg; cfg.indirectBuffer( crg::IndirectBuffer{ indirectv, sizeof( VkDrawIndirectCommand ) } ); cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); return std::make_unique< crg::ComputePass >( pass, context, runGraph , crg::ru::Config{}, std::move( cfg ) ); } ); auto buffera = testPass1.addClearableOutputStorageBuffer( bufferv, 1u ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto depth = graph.createImage( test::createImage( "depth", crg::PixelFormat::eD32_SFLOAT ) ); auto depthStencil = graph.createImage( test::createImage( "depthStencil", crg::PixelFormat::eD32_SFLOAT_S8_UINT ) ); auto buffer1 = graph.createBuffer( test::createBuffer( "buffer1" ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto depthv = graph.createView( test::createView( "depthv", depth, crg::PixelFormat::eD32_SFLOAT, 0u, 1u, 0u, 1u ) ); auto depthStencilv = graph.createView( test::createView( "depthStencilv", depthStencil, crg::PixelFormat::eD32_SFLOAT_S8_UINT, 0u, 1u, 0u, 1u ) ); auto buffer1v = graph.createView( test::createView( "buffer1v", buffer1 ) ); crg::ComputePass * computePass{}; auto & testPass2 = graph.createPass( "Pass2" , [&computePass]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::cp::Config cfg; cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); auto res = std::make_unique< crg::ComputePass >( pass, context, runGraph , crg::ru::Config{ 1u, true }, std::move( cfg ) ); computePass = res.get(); return res; } ); testPass2.addInputUniformBuffer( buffer1v, 0u ); testPass2.addInputUniform( *buffera, 1u ); auto resulta = testPass2.addClearableOutputStorageImage( resultv, 2u ); auto deptha = testPass2.addClearableOutputStorageImage( depthv, 3u ); auto buffer2 = graph.createBuffer( test::createBuffer( "buffer2" ) ); auto buffer3 = graph.createBuffer( test::createBuffer( "buffer3" ) ); auto buffer4 = graph.createBuffer( test::createBuffer( "buffer4" ) ); auto buffer5 = graph.createBuffer( test::createBuffer( "buffer5" ) ); auto buffer6 = graph.createBuffer( test::createBuffer( "buffer6" ) ); auto buffer7 = graph.createBuffer( test::createBuffer( "buffer7" ) ); auto buffer2v = graph.createView( test::createView( "buffer2v", buffer2 ) ); auto buffer3v = graph.createView( test::createView( "buffer3v", buffer3 ) ); auto buffer4v = graph.createView( test::createView( "buffer4v", buffer4, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto buffer5v = graph.createView( test::createView( "buffer5v", buffer5, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto buffer6v = graph.createView( test::createView( "buffer6v", buffer6, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto buffer7v = graph.createView( test::createView( "buffer7v", buffer7, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto buffer2a = crg::Attachment::createDefault( buffer2v ); auto buffer3a = crg::Attachment::createDefault( buffer3v ); auto buffer5a = crg::Attachment::createDefault( buffer5v ); auto buffer7a = crg::Attachment::createDefault( buffer7v ); auto depthStencila = crg::Attachment::createDefault( depthStencilv ); auto & testPass3 = graph.createPass( "Pass3" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::cp::Config cfg; cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); return std::make_unique< crg::ComputePass >( pass, context, runGraph , crg::ru::Config{}, std::move( cfg ) ); } ); testPass3.addInputStorage( buffer2a, 2u ); testPass3.addInOutStorage( buffer3a, 3u ); testPass3.addInputUniformBuffer( buffer4v, 4u ); testPass3.addInputStorage( buffer5a, 5u ); testPass3.addOutputStorageBuffer( buffer6v, 6u ); testPass3.addInOutStorage( buffer7a, 7u ); testPass3.addImplicit( *resulta, crg::ImageLayout::eShaderReadOnly ); testPass3.addImplicit( *deptha, crg::ImageLayout::eShaderReadOnly ); testPass3.addImplicit( depthStencila, crg::ImageLayout::eShaderReadOnly ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); require( computePass ) checkNoThrow( computePass->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( computePass->resetCommandBuffer( 0u ) ) checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) checkNoThrow( graph.getFinalAccessState( buffer1v ) ) testEnd() } TEST( RunnablePass, ComputePassTransitions ) { testBegin( "testComputePassTransitions" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, "/" + testCounts.testName }; auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto depth = graph.createImage( test::createImage( "depth", crg::PixelFormat::eD32_SFLOAT ) ); auto buffer1 = graph.createBuffer( test::createBuffer( "buffer1" ) ); auto buffer2 = graph.createBuffer( test::createBuffer( "buffer2" ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto depthv = graph.createView( test::createView( "depthv", depth, crg::PixelFormat::eD32_SFLOAT, 0u, 1u, 0u, 1u ) ); auto buffer1v = graph.createView( test::createView( "buffer1v", buffer1 ) ); auto buffer2v = graph.createView( test::createView( "buffer2v", buffer2 ) ); auto & testPass1 = graph.createPass( "Pass1" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::cp::Config cfg; cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); return std::make_unique< crg::ComputePass >( pass, context, runGraph , crg::ru::Config{ 1u, true }, std::move( cfg ) ); } ); auto resulta = testPass1.addClearableOutputStorageImage( resultv, 0u ); auto deptha = testPass1.addClearableOutputStorageImage( depthv, 1u ); auto buffer1a = testPass1.addClearableOutputStorageBuffer( buffer1v, 2u ); auto buffer2a = testPass1.addClearableOutputStorageBuffer( buffer2v, 3u ); auto & testPass2 = graph.createPass( "Pass2" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::cp::Config cfg; cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); return std::make_unique< crg::ComputePass >( pass, context, runGraph , crg::ru::Config{}, std::move( cfg ) ); } ); testPass2.addImplicit( *resulta, crg::ImageLayout::eShaderReadOnly ); testPass2.addImplicit( *deptha, crg::ImageLayout::eShaderReadOnly ); testPass2.addInputStorage( *buffer1a, 1u ); testPass2.addImplicit( *buffer2a, crg::AccessState{} ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); testEnd() } TEST( RunnablePass, RenderPass_ORcl ) { testBegin( "testRenderPass_ORcl" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto sampled = graph.createImage( test::createImage( "sampled", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto sampledv = graph.createView( test::createView( "sampledv", sampled, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto & testPass = graph.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > } ); } ); testPass.addInputSampledImage( sampledv, 0u ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, RenderPass_ORcl_DefCont ) { testBegin( "testRenderPass_ORcl_DefCont" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto & testPass = graph.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > , crg::defaultV< crg::RenderPass::GetSubpassContentsCallback > } ); } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, RenderPass_MergedImageViews_ORcl ) { testBegin( "testRenderPass_MergedImageViews_ORcl" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & group = graph.createPassGroup( "testGroup" ); auto result = group.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 2u ) ); auto result1v = group.createView( test::createView( "result1v", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto result2v = group.createView( test::createView( "result2v", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 1u, 1u ) ); auto resultv = group.mergeViews( { result1v, result2v } ); auto & testPass = group.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > , crg::defaultV< crg::RenderPass::GetSubpassContentsCallback > } ); } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); checkEqual( graph.getFinalLayoutState( resultv ).layout, crg::ImageLayout::eColorAttachment ) } testEnd() } TEST( RunnablePass, RenderPass_MergedImageViews_CubeARray_ORcl ) { testBegin( "testRenderPass_MergedImageViews_CubeARray_ORcl" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & group = graph.createPassGroup( "testGroup" ); auto result = group.createImage( test::createImageCube( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 12u ) ); auto result1v = group.createView( crg::ImageViewData{ "result1v" , result , crg::ImageViewCreateFlags::eNone , crg::ImageViewType::e2D , getFormat( result ) , { getAspectMask( getFormat( result ) ), 0u, 1u, 0u, 6u } } ); auto result2v = group.createView( crg::ImageViewData{ "result2v" , result , crg::ImageViewCreateFlags::eNone , crg::ImageViewType::e2D , getFormat( result ) , { getAspectMask( getFormat( result ) ), 0u, 1u, 6u, 6u } } ); auto resultv = group.mergeViews( { result1v, result2v } ); auto & testPass = group.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > , crg::defaultV< crg::RenderPass::GetSubpassContentsCallback > } ); } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); checkEqual( graph.getFinalLayoutState( resultv ).layout, crg::ImageLayout::eColorAttachment ) } testEnd() } TEST( RunnablePass, RenderPass_MergedBufferViews_ORcl ) { testBegin( "testRenderPass_MergedBufferViews_ORcl" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto & group = graph.createPassGroup( "testGroup" ); auto result = group.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto buffer = group.createBuffer( test::createBuffer( "buffer" ) ); auto resultv = group.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto buffer1v = group.createView( test::createView( "buffer1v", buffer, 0, 512u ) ); auto buffer2v = group.createView( test::createView( "buffer2v", buffer, 512u, 512u ) ); auto bufferv = group.mergeViews( { buffer1v, buffer2v } ); auto & testPass = group.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > , crg::defaultV< crg::RenderPass::GetSubpassContentsCallback > } ); } ); testPass.addOutputStorageBuffer( bufferv, 0u ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); checkEqual( graph.getFinalAccessState( bufferv ).access, crg::AccessFlags::eShaderWrite ) } testEnd() } TEST( RunnablePass, RenderPass_MergedImageAttachs_ORcl ) { testBegin( "testRenderPass_MergedImageAttachs_ORcl" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto intermediate = graph.createImage( test::createImage( "intermediate", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 2u ) ); auto intermediate1v = graph.createView( test::createView( "intermediate1v", intermediate, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto intermediate2v = graph.createView( test::createView( "intermediate2v", intermediate, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 1u, 1u ) ); auto & testPass1 = graph.createPass( "Pass1" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > , crg::defaultV< crg::RenderPass::GetSubpassContentsCallback > } ); } ); auto intermediate1a = testPass1.addOutputColourTarget( intermediate1v ); auto intermediate2a = testPass1.addOutputColourTarget( intermediate2v ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto & testPass2 = graph.createPass( "Pass2" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > , crg::defaultV< crg::RenderPass::GetSubpassContentsCallback > } ); } ); auto intermediatea = graph.mergeAttachments( { intermediate1a, intermediate2a } ); testPass2.addInputSampled( *intermediatea, 0u ); testPass2.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); checkEqual( graph.getFinalLayoutState( intermediatea->view() ).layout, crg::ImageLayout::eShaderReadOnly ) } testEnd() } TEST( RunnablePass, RenderPass_MergedBufferAttachs_ORcl ) { testBegin( "testRenderPass_MergedBufferAttachs_ORcl" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto buffer = graph.createBuffer( test::createBuffer( "buffer" ) ); auto buffer1v = graph.createView( test::createView( "buffer1v", buffer, 0u, 512u ) ); auto buffer2v = graph.createView( test::createView( "buffer2v", buffer, 512u, 512u ) ); auto & testPass1 = graph.createPass( "Pass1" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::cp::Config cfg; cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); return std::make_unique< crg::ComputePass >( pass, context, runGraph , crg::ru::Config{ 1u, true }, std::move( cfg ) ); } ); auto buffer1a = testPass1.addOutputStorageBuffer( buffer1v, 0u ); auto buffer2a = testPass1.addOutputStorageBuffer( buffer2v, 1u ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto & testPass2 = graph.createPass( "Pass2" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > , crg::defaultV< crg::RenderPass::GetSubpassContentsCallback > } ); } ); auto buffera = graph.mergeAttachments( { buffer1a, buffer2a } ); testPass2.addInputStorage( *buffera, 0u ); testPass2.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); checkEqual( graph.getFinalAccessState( buffera->buffer() ).access, crg::AccessFlags::eShaderRead ) } testEnd() } TEST( RunnablePass, RenderPass_ORdp ) { testBegin( "testRenderPass_ORdp" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto depth = graph.createImage( test::createImage( "depth", crg::PixelFormat::eD32_SFLOAT ) ); auto depthv = graph.createView( test::createView( "depthv", depth, crg::PixelFormat::eD32_SFLOAT, 0u, 1u, 0u, 1u ) ); auto & testPass = graph.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > } ); } ); testPass.addOutputDepthTarget ( depthv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, RenderPass_ORcl_ORdp ) { testBegin( "testRenderPass_ORcl_ORdp" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; uint32_t passIndex{}; auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto depth = graph.createImage( test::createImage( "depth", crg::PixelFormat::eD32_SFLOAT ) ); auto depthv = graph.createView( test::createView( "depthv", depth, crg::PixelFormat::eD32_SFLOAT, 0u, 1u, 0u, 1u ) ); crg::Extent2D extent{ getExtent( resultv ).width, getExtent( resultv ).height }; auto & testPass = graph.createPass( "Pass" , [&passIndex, extent]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > , crg::defaultV< crg::RenderPass::GetSubpassContentsCallback > , crg::RenderPass::GetPassIndexCallback( [passIndex](){ return passIndex; } ) } , extent , crg::ru::Config{ 2u } ); } ); testPass.addOutputColourTarget( resultv ); testPass.addOutputDepthTarget ( depthv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); passIndex = 1u - passIndex; checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) } testEnd() } TEST( RunnablePass, RenderPass ) { testBegin( "testRenderPass" ) { crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto result1 = graph.createImage( test::createImage( "result1", crg::PixelFormat::eR16G16B16A16_SFLOAT, 1u, 2u ) ); auto result1v = graph.createView( test::createView( "result1v", result1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto result2v = graph.createView( test::createView( "result2v", result1, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); auto & testPass = graph.createPass( "Pass" , []( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { return std::make_unique< crg::RenderPass >( pass, context, runGraph , crg::RenderPass::Callbacks{ crg::defaultV< crg::RunnablePass::InitialiseCallback > , crg::defaultV< crg::RunnablePass::RecordCallback > } ); } ); testPass.addOutputColourTarget( graph.mergeViews( { result1v, result2v } ) ); testPass.addOutputColourTarget( graph.mergeViews( { result1v, result2v }, false, true ) ); testPass.addOutputColourTarget( graph.mergeViews( { result1v, result2v }, true, false ) ); testPass.addOutputColourTarget( graph.mergeViews( { result1v, result2v }, false, false ) ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); } testEnd() } TEST( RunnablePass, RenderQuad ) { testBegin( "testRenderQuad" ) crg::ResourceHandler handler; auto result = handler.createImageId( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = handler.createViewId( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); { crg::FrameGraph graph{ handler, testCounts.testName }; crg::RenderQuad * renderQuad{}; uint32_t passIndex{}; auto & testPass = graph.createPass( "Pass" , [&renderQuad, &passIndex]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::rq::Config cfg; cfg.passIndex( &passIndex ); cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 2u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); auto res = std::make_unique< crg::RenderQuad >( pass, context, runGraph , crg::ru::Config{ 2u, true }, std::move( cfg ) ); renderQuad = res.get(); return res; } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); require( renderQuad ) passIndex = 1u - passIndex; checkNoThrow( renderQuad->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) } testEnd() } TEST( RunnablePass, RenderQuad_Indirect ) { testBegin( "testRenderQuad_Indirect" ) crg::ResourceHandler handler; auto result = handler.createImageId( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = handler.createViewId( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); { crg::FrameGraph graph{ handler, testCounts.testName }; crg::RenderQuad * renderQuad{}; auto indirect = graph.createBuffer( test::createBuffer( "indirect" ) ); auto indirectv = graph.createView( test::createView( "indirectv", indirect ) ); auto & testPass = graph.createPass( "Pass" , [&renderQuad, &indirectv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::rq::Config cfg; cfg.indirectBuffer( crg::IndirectBuffer{ indirectv, sizeof( VkDrawIndirectCommand ) } ); cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); auto res = std::make_unique< crg::RenderQuad >( pass, context, runGraph , crg::ru::Config{ 2u, true }, std::move( cfg ) ); renderQuad = res.get(); return res; } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); require( renderQuad ) checkNoThrow( renderQuad->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) } testEnd() } TEST( RunnablePass, RenderMesh ) { testBegin( "testRenderMesh" ) crg::ResourceHandler handler; auto result = handler.createImageId( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = handler.createViewId( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); { crg::FrameGraph graph{ handler, testCounts.testName }; crg::RenderMesh * renderMesh{}; uint32_t passIndex{}; auto & testPass = graph.createPass( "Pass" , [&renderMesh, &passIndex]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::rm::Config cfg; cfg.getPassIndex( crg::RunnablePass::GetPassIndexCallback( [&passIndex](){ return passIndex; } ) ); cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 2u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); auto res = std::make_unique< crg::RenderMesh >( pass, context, runGraph , crg::ru::Config{ 2u, true }, std::move( cfg ) ); renderMesh = res.get(); return res; } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); require( renderMesh ) passIndex = 1u - passIndex; checkNoThrow( renderMesh->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) } testEnd() } TEST( RunnablePass, RenderMesh_Vertex ) { testBegin( "testRenderMesh_Vertex" ) crg::ResourceHandler handler; auto result = handler.createImageId( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = handler.createViewId( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); { crg::FrameGraph graph{ handler, testCounts.testName }; crg::RenderMesh * renderMesh{}; auto vertex = graph.createBuffer( test::createBuffer( "vertex" ) ); auto vertexv = graph.createView( test::createView( "vertexv", vertex ) ); auto & testPass = graph.createPass( "Pass" , [&renderMesh, &vertexv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::rm::Config cfg; cfg.vertexBuffer( crg::VertexBuffer{ vertexv } ); cfg.baseConfig( crg::pp::Config{} .programs( { crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} } } ) ); auto res = std::make_unique< crg::RenderMesh >( pass, context, runGraph , crg::ru::Config{ 1u, true }, std::move( cfg ) ); renderMesh = res.get(); return res; } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); require( renderMesh ) checkNoThrow( renderMesh->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) } testEnd() } TEST( RunnablePass, RenderMesh_Vertex_Index ) { testBegin( "testRenderMesh_Vertex_Index" ) crg::ResourceHandler handler; auto result = handler.createImageId( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = handler.createViewId( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); { crg::FrameGraph graph{ handler, testCounts.testName }; auto vertex = graph.createBuffer( test::createBuffer( "vertex" ) ); auto index = graph.createBuffer( test::createBuffer( "index" ) ); auto vertexv = graph.createView( test::createView( "vertexv", vertex ) ); auto indexv = graph.createView( test::createView( "indexv", index ) ); crg::RenderMesh * renderMesh{}; auto & testPass = graph.createPass( "Pass" , [&renderMesh, &vertexv, &indexv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::rm::Config cfg; auto vb = crg::VertexBuffer{ vertexv }; cfg.vertexBuffer( std::move( vb ) ); cfg.indexBuffer( crg::IndexBuffer{ indexv } ); cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); auto res = std::make_unique< crg::RenderMesh >( pass, context, runGraph , crg::ru::Config{ 1u, true }, std::move( cfg ) ); renderMesh = res.get(); return res; } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); require( renderMesh ) checkNoThrow( renderMesh->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) } testEnd() } TEST( RunnablePass, RenderMesh_Indirect ) { testBegin( "testRenderMesh_Indirect" ) crg::ResourceHandler handler; auto result = handler.createImageId( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = handler.createViewId( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); { crg::FrameGraph graph{ handler, testCounts.testName }; auto indirect = graph.createBuffer( test::createBuffer( "indirect" ) ); auto indirectv = graph.createView( test::createView( "indirectv", indirect ) ); crg::RenderMesh * renderMesh{}; auto & testPass = graph.createPass( "Pass" , [&renderMesh, &indirectv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::rm::Config cfg; cfg.indirectBuffer( crg::IndirectBuffer{ indirectv, sizeof( VkDrawIndirectCommand ) } ); cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); auto res = std::make_unique< crg::RenderMesh >( pass, context, runGraph , crg::ru::Config{ 1u, true }, std::move( cfg ) ); renderMesh = res.get(); return res; } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); require( renderMesh ) checkNoThrow( renderMesh->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) } testEnd() } TEST( RunnablePass, RenderMesh_Indirect_Index ) { testBegin( "testRenderMesh_Indirect_Index" ) crg::ResourceHandler handler; auto result = handler.createImageId( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = handler.createViewId( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); { crg::FrameGraph graph{ handler, testCounts.testName }; auto indirect = graph.createBuffer( test::createBuffer( "indirect" ) ); auto index = graph.createBuffer( test::createBuffer( "index" ) ); auto indirectv = graph.createView( test::createView( "indirectv", indirect ) ); auto indexv = graph.createView( test::createView( "indexv", index ) ); crg::RenderMesh * renderMesh{}; auto & testPass = graph.createPass( "Pass" , [&renderMesh, &indirectv, &indexv]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::rm::Config cfg; cfg.indirectBuffer( crg::IndirectBuffer{ indirectv, sizeof( VkDrawIndexedIndirectCommand ) } ); cfg.indexBuffer( crg::IndexBuffer{ indexv } ); cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ){ return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); auto res = std::make_unique< crg::RenderMesh >( pass, context, runGraph , crg::ru::Config{ 1u, true }, std::move( cfg ) ); renderMesh = res.get(); return res; } ); testPass.addOutputColourTarget( resultv ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); require( renderMesh ) checkNoThrow( renderMesh->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( VkQueue{} ) ) } testEnd() } TEST( RunnablePass, RenderTexturedMesh ) { testBegin( "testRenderTexturedMesh" ) crg::ResourceHandler handler; crg::FrameGraph graph{ handler, testCounts.testName }; auto sampled = graph.createImage( test::createImage( "sampled", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto sampledv = graph.createView( test::createView( "sampledv", sampled, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); crg::RenderQuad * renderQuad{}; auto & testQuad = graph.createPass( "Quad" , [&renderQuad]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::rq::Config cfg; cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ) { return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); auto res = std::make_unique< crg::RenderQuad >( pass, context, runGraph , crg::ru::Config{ 2u, true }, std::move( cfg ) ); renderQuad = res.get(); return res; } ); auto sampledAttach = testQuad.addOutputColourTarget( sampledv ); auto result = graph.createImage( test::createImage( "result", crg::PixelFormat::eR16G16B16A16_SFLOAT ) ); auto resultv = graph.createView( test::createView( "resultv", result, crg::PixelFormat::eR16G16B16A16_SFLOAT, 0u, 1u, 0u, 1u ) ); crg::RenderMesh * renderMesh{}; auto & testMesh = graph.createPass( "Mesh" , [&renderMesh]( crg::FramePass const & pass , crg::GraphContext & context , crg::RunnableGraph & runGraph ) { crg::rm::Config cfg; cfg.baseConfig( crg::pp::Config{} .programCreator( crg::ProgramCreator{ 1u , []( uint32_t ) { return crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }; } } ) ); auto res = std::make_unique< crg::RenderMesh >( pass, context, runGraph , crg::ru::Config{ 1u, true }, std::move( cfg ) ); renderMesh = res.get(); return res; } ); testMesh.addOutputColourTarget( resultv ); testMesh.addInputSampled( *sampledAttach, 0u ); auto runnable = graph.compile( getContext() ); test::checkRunnable( testCounts, runnable ); require( renderQuad ) require( renderMesh ) checkNoThrow( renderQuad->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( renderQuad->resetPipelineLayout( {}, {}, crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( renderMesh->resetPipeline( crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( renderMesh->resetPipelineLayout( {}, {}, crg::VkPipelineShaderStageCreateInfoArray{ VkPipelineShaderStageCreateInfo{} }, 0u ) ) checkNoThrow( runnable->record() ) checkNoThrow( runnable->run( crg::SemaphoreWait{ VkSemaphore( 1 ), crg::PipelineStageFlags::eAllGraphics }, VkQueue{} ) ) checkEqual( graph.getFinalLayoutState( sampledv, 0u ).layout, crg::ImageLayout::eShaderReadOnly ) checkEqual( graph.getDefaultGroup().getFinalLayoutState( sampledv, 0u ).layout, crg::ImageLayout::eShaderReadOnly ) testEnd() } } testSuiteMain() ================================================ FILE: vcpkg.json ================================================ { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", "name": "rendergraph", "version": "1.4.1", "builtin-baseline": "e140b1fde236eb682b0d47f905e65008a191800f", "dependencies": [ "vulkan-headers" ], "features": { "tests": { "description": "Unit tests.", "dependencies": [ "fmt", "gtest" ] } } }