diff --git a/.codecov.yml b/.codecov.yml index 0819f622a..64c08511f 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -15,6 +15,10 @@ coverage: informational: true only_pulls: true +ignore: + - "Tests" + - "Apps" + component_management: individual_components: - component_id: common-primitives diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 871e29774..19275eaa8 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -31,9 +31,9 @@ on: env: product_ver_major: 0 product_ver_minor: 7 - product_ver_patch: 1 + product_ver_patch: 2 product_ver_build: ${{ github.run_number }} - tracy_release_version: "0.9" + tracy_release_version: "0.9.1" # TSC Invariant check is disabled to allow running Catch test executables only for tests list query by CTest TRACY_NO_INVARIANT_CHECK: 1 @@ -215,13 +215,20 @@ jobs: working-directory: ${{ env.INSTALL_DIR }}/Tests shell: bash run: | + set +e result_ext='_result.xml' + result_error_level=0 echo Running all unit-tests in directory $PWD for test_exe in *Test do ./$test_exe -r junit -o "$test_exe$result_ext" - echo - $test_exe - completed with $? exit code + last_error_level=$? + echo - $test_exe - completed with $last_error_level exit code + if [ $last_error_level != 0 ]; then + result_error_level=$last_error_level + fi done + exit $result_error_level - name: Run Unit-Tests on Windows if: ${{ matrix.run_tests && matrix.os_name == 'Windows' }} @@ -230,11 +237,15 @@ jobs: run: | setlocal enabledelayedexpansion echo Running unit-tests in directory "%cd%" + set /A result_error_level=0 for /r "." %%a in (*Test.exe) do ( "%%~fa" -r junit -o "%%~fa_result.xml" echo - %%~na - completed with !errorlevel! exit status + if not !errorlevel!==0 ( + set /A result_error_level=!errorlevel! + ) ) - exit 0 + exit !result_error_level! - name: Upload Test Results Artifact uses: actions/upload-artifact@v3 @@ -243,11 +254,12 @@ jobs: name: MethaneKit_${{ matrix.name }}_TestResults_${{ env.product_ver_major }}.${{ env.product_ver_minor }}.${{ env.product_ver_patch }}.${{ env.product_ver_build }} path: ${{ env.INSTALL_DIR }}/Tests/*_result.xml - - name: Upload Build Log and Test Tesults to Testspace server + - name: Upload Test Results and Build Log to Testspace server if: ${{ matrix.run_tests && (success() || failure()) }} run: testspace "[ ${{ matrix.name }} ]${{ env.INSTALL_DIR }}/Tests/*_result.xml" "[ ${{ matrix.name }} ]${{ env.BUILD_LOG_FILE }}" - name: Add README and BUILD files + if: ${{ success() || failure() }} shell: bash run: | cp README.md $INSTALL_DIR/README.md @@ -259,26 +271,28 @@ jobs: echo - Builder agent ${{ matrix.os }} system information: >> $INSTALL_DIR/Build-Info.txt - name: Add Windows System Information to BUILD file - if: ${{ matrix.os_name == 'Windows' }} + if: ${{ matrix.os_name == 'Windows' && (success() || failure()) }} run: systeminfo >> Build\Output\${{ matrix.config_preset }}\Install\Build-Info.txt - name: Add Unix System Information to BUILD file - if: ${{ matrix.os_name != 'Windows' }} + if: ${{ matrix.os_name != 'Windows' && (success() || failure()) }} run: uname -a >>$INSTALL_DIR/Build-Info.txt - name: Download Tracy release - if: ${{ matrix.add_tracy_app }} + if: ${{ matrix.add_tracy_app && (success() || failure()) }} shell: bash run: | curl -sSLo Tracy.7z https://github.com/MethanePowered/Tracy/releases/download/v${{ env.tracy_release_version }}/Tracy-${{ matrix.os_name }}-v${{ env.tracy_release_version }}.7z 7z x Tracy.7z -o$INSTALL_DIR/Apps - name: Archive Build Artifacts + if: ${{ success() || failure() }} shell: bash working-directory: ${{ env.INSTALL_DIR }} run: 7z a -t7z -mx=9 MethaneKit_${{ matrix.name }}.7z * - name: Upload Archived Build Artifacts + if: ${{ success() || failure() }} uses: actions/upload-artifact@v3 with: name: MethaneKit_${{ matrix.name }}_${{ env.product_ver_major }}.${{ env.product_ver_minor }}.${{ env.product_ver_patch }}.${{ env.product_ver_build }} diff --git a/.github/workflows/ci-sonar-scan.yml b/.github/workflows/ci-sonar-scan.yml index 105e10e7f..9f9bbd3f6 100644 --- a/.github/workflows/ci-sonar-scan.yml +++ b/.github/workflows/ci-sonar-scan.yml @@ -36,7 +36,7 @@ on: env: product_ver_major: 0 product_ver_minor: 7 - product_ver_patch: 1 + product_ver_patch: 2 product_ver_build: ${{ github.run_number }} sonar_server_url: "https://sonarcloud.io" sonar_organization: methane-powered @@ -159,13 +159,15 @@ jobs: throw 'OpenCppCoverage/OpenCppCoverage.exe executable was not found in unpacked content!' } - - name: Run all unit-tests from install directory with code coverage using OpenCppCoverage on Windows + - name: Run all unit-tests with OpenCppCoverage code coverage on Windows if: ${{ matrix.os_name == 'windows' }} shell: cmd working-directory: 'Build\Output\${{ matrix.config_preset }}\Install\Tests' run: | + chcp 65001 #set code page to utf-8 setlocal enabledelayedexpansion set open_cpp_coverage_exe=OpenCppCoverage\OpenCppCoverage.exe + set test_results= if not exist "%open_cpp_coverage_exe%" ( echo File path "%open_cpp_coverage_exe%" does not exist! exit 101 @@ -181,43 +183,73 @@ jobs: if not !errorlevel!==0 ( set /A result_error_level=!errorlevel! ) + if .!test_results!==. ( + set test_results=Build/Output/${{ matrix.config_preset }}/Install/Tests/Results/%%~na.xml + ) else ( + set test_results=!test_results!,Build/Output/${{ matrix.config_preset }}/Install/Tests/Results/%%~na.xml + ) ) + echo Test Result Files: %test_results% + echo test_results=%test_results%>> %GITHUB_ENV% exit !result_error_level! - - name: Run unit-tests from install directory on Linux + - name: Run all unit-tests with GCov code coverage on Linux if: ${{ matrix.os_name == 'linux' }} working-directory: 'Build/Output/${{ matrix.config_preset }}/Install/Tests' run: | + set +e result_ext='.xml' + test_results='' + result_error_level=0 echo Running unit-tests in directory $PWD mkdir Results for test_exe in *Test do ./$test_exe -r sonarqube -o "Results/$test_exe$result_ext" - echo - $test_exe - completed with $? exit status + last_error_level=$? + echo - $test_exe - completed with $last_error_level exit status + if [ $last_error_level != 0 ]; then + result_error_level=$last_error_level + fi + if [ -f "$PWD/Results/$test_exe$result_ext" ]; then + test_results+="$PWD/Results/$test_exe$result_ext," + fi done + echo "Test Result Files: $test_results" + echo "test_results=$test_results" >> $GITHUB_ENV + exit $result_error_level - name: Collect tests code coverage using ctest and gcov/lcov on Linux - if: ${{ matrix.os_name == 'linux' }} + if: ${{ matrix.os_name == 'linux' && (success() || failure()) }} run: | set -o pipefail cmake --build --preset ${{ matrix.build_preset }} --target MethaneTestCoverage 2>&1 | tee $COVERAGE_LOG_FILE - - name: Run all unit-tests from install directory with LCov code coverage on MacOS + - name: Run all unit-tests with LCov code coverage on MacOS if: ${{ matrix.os_name == 'macosx' }} working-directory: 'Build/Output/${{ matrix.config_preset }}/Install/Tests' run: | + set +e + result_error_level=0 result_ext='.xml' prof_data_ext='.profdata' prof_raw_ext='.profraw' lcov_ext='.lcov' + test_results='' echo Running unit-tests and Converting LLVM code coverage data to lcov text format in directory $PWD mkdir Results mkdir Coverage for test_exe in *Test do ./$test_exe -r sonarqube -o "Results/$test_exe$result_ext" - echo - $test_exe - completed with $? exit status + last_error_level=$? + echo - $test_exe - completed with $last_error_level exit status + if [ $last_error_level != 0 ]; then + result_error_level=$last_error_level + fi + if [ -f "$PWD/Results/$test_exe$result_ext" ]; then + test_results+="$PWD/Results/$test_exe$result_ext," + fi if [ ! -f default.profraw ]; then continue fi @@ -226,10 +258,14 @@ jobs: xcrun llvm-cov export -format lcov -instr-profile="$test_exe$prof_data_ext" -arch=x86_64 ./$test_exe > "./Coverage/$test_exe$lcov_ext" echo - Converted code coverage from "$test_exe$prof_raw_ext" to lcov text format "./Coverage/$test_exe$lcov_ext", $? exit status done + echo "Test Result Files: $test_results" + echo "test_results=$test_results" >> $GITHUB_ENV echo List of generated coverage files in directory $PWD/Coverage ls -la ./Coverage + exit $result_error_level - name: Generate Code Coverage Reports + if: ${{ success() || failure() }} uses: danielpalme/ReportGenerator-GitHub-Action@5.1.13 with: reports: ${{ matrix.tests_coverage_reports }} @@ -239,16 +275,18 @@ jobs: tag: '${{ env.product_ver_major }}.${{ env.product_ver_minor }}.${{ env.product_ver_patch }}.${{ env.product_ver_build }}' - name: Upload Code Coverage Cobertura Report + if: ${{ success() || failure() }} uses: actions/upload-artifact@v3 with: name: MethaneKit_${{ matrix.name }}_CoverageResults path: Build/Output/${{ matrix.config_preset }}/Install/Tests/Coverage/Report/Cobertura.xml - name: Upload Build Log and Code Coverage to Testspace server - if: ${{ always() }} + if: ${{ success() || failure() }} run: testspace "[ ${{ matrix.name }} ]Build/Output/${{ matrix.config_preset }}/Install/Tests/Coverage/Report/Cobertura.xml" "[ ${{ matrix.name }} ]${{ env.BUILD_LOG_FILE }}" - name: Upload Code Coverage to CodeCov server + if: ${{ success() || failure() }} uses: codecov/codecov-action@v3 with: files: Build/Output/${{ matrix.config_preset }}/Install/Tests/Coverage/Report/Cobertura.xml @@ -256,6 +294,7 @@ jobs: name: ${{ matrix.name }} - name: Run Sonar Scanner + if: ${{ success() || failure() }} shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -273,7 +312,7 @@ jobs: SONAR_SCAN_CMD="$SONAR_SCAN_CMD --define sonar.projectKey=${{ matrix.sonar_project_key }}" SONAR_SCAN_CMD="$SONAR_SCAN_CMD --define sonar.projectVersion=${{ env.product_ver_major }}.${{ env.product_ver_minor }}.${{ env.product_ver_patch }}.${{ env.product_ver_build }}" SONAR_SCAN_CMD="$SONAR_SCAN_CMD --define sonar.cfamily.compile-commands=Build/Output/${{ matrix.config_preset }}/Build/compile_commands.json" - SONAR_SCAN_CMD="$SONAR_SCAN_CMD --define sonar.testExecutionReportPaths=$TESTS_DIR/Results/MethaneDataTypesTest.xml,$TESTS_DIR/Results/MethaneDataEventsTest.xml,$TESTS_DIR/Results/MethaneDataRangeSetTest.xml,$TESTS_DIR/Results/MethanePlatformInputTest.xml,$TESTS_DIR/Results/MethaneGraphicsTypesTest.xml,$TESTS_DIR/Results/MethaneGraphicsCameraTest.xml,$TESTS_DIR/Results/MethaneUserInterfaceTypesTest.xml" + SONAR_SCAN_CMD="$SONAR_SCAN_CMD --define sonar.testExecutionReportPaths=${{ env.test_results }}" SONAR_SCAN_CMD="$SONAR_SCAN_CMD --define sonar.coverageReportPaths=$TESTS_DIR/Coverage/Report/SonarQube.xml" SONAR_SCAN_CMD="$SONAR_SCAN_CMD --define sonar.scm.revision=${{ github.sha }}" if [ "${{ github.event_name }}" == "pull_request" ]; then @@ -293,13 +332,13 @@ jobs: done - name: Archive Scan Artifacts - if: ${{ always() }} + if: ${{ success() || failure() }} shell: bash working-directory: Build/Output/${{ matrix.config_preset }}/Install run: 7z a -t7z -mx=9 MethaneKit_${{ matrix.name }}.7z * - name: Upload Archived Scan Artifacts - if: ${{ always() }} + if: ${{ success() || failure() }} uses: actions/upload-artifact@v3 with: name: MethaneKit_${{ matrix.name }}_${{ env.product_ver_major }}.${{ env.product_ver_minor }}.${{ env.product_ver_patch }}.${{ env.product_ver_build }} diff --git a/.github/workflows/ci-tests-report.yml b/.github/workflows/ci-tests-report.yml index 2c012955d..8fb2c0108 100644 --- a/.github/workflows/ci-tests-report.yml +++ b/.github/workflows/ci-tests-report.yml @@ -39,7 +39,7 @@ jobs: - name: Add PR Comment with Test Results uses: marocchino/sticky-pull-request-comment@v2 - if: ${{ github.event.workflow_run.event == 'pull_request' && steps.test-reporter.outputs.conclusion == 'success' }} + if: ${{ github.event.workflow_run.event == 'pull_request' && steps.test-reporter.outputs.conclusion == 'success' && (success() || failure()) }} with: number: ${{ github.event.workflow_run.pull_requests[0].number }} header: ${{ matrix.name }} Test Results @@ -53,7 +53,7 @@ jobs: - :stopwatch: ${{ steps.test-reporter.outputs.time }} ms. run duration - name: Add Commit Comment with Test Results - if: ${{ github.event.workflow_run.event == 'push' && steps.test-reporter.outputs.conclusion == 'success' }} + if: ${{ github.event.workflow_run.event == 'push' && steps.test-reporter.outputs.conclusion == 'success' && (success() || failure()) }} uses: peter-evans/commit-comment@v2 with: sha: ${{ github.event.workflow_run.head_sha }} diff --git a/.idea/runConfigurations/08_ConsoleCompute.xml b/.idea/runConfigurations/08_ConsoleCompute.xml new file mode 100644 index 000000000..4a624dfd1 --- /dev/null +++ b/.idea/runConfigurations/08_ConsoleCompute.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Graphics_RHI_Test.xml b/.idea/runConfigurations/Graphics_RHI_Test.xml new file mode 100644 index 000000000..5fc19bf78 --- /dev/null +++ b/.idea/runConfigurations/Graphics_RHI_Test.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/Apps/02-HelloCube/HelloCubeApp.cpp b/Apps/02-HelloCube/HelloCubeApp.cpp index e806cf2e5..08bedff54 100644 --- a/Apps/02-HelloCube/HelloCubeApp.cpp +++ b/Apps/02-HelloCube/HelloCubeApp.cpp @@ -79,9 +79,10 @@ class HelloCubeApp final // NOSONAR - destructor required Camera m_camera; #ifdef UNIFORMS_BUFFER_ENABLED - hlslpp::Uniforms m_shader_uniforms { }; - const Rhi::SubResources m_shader_uniforms_subresources{ - { reinterpret_cast(&m_shader_uniforms), sizeof(hlslpp::Uniforms) } // NOSONAR + hlslpp::Uniforms m_shader_uniforms { }; + const Rhi::SubResource m_shader_uniforms_subresource{ + reinterpret_cast(&m_shader_uniforms), // NOSONAR + sizeof(hlslpp::Uniforms) }; Rhi::BufferSet m_vertex_buffer_set; #else @@ -179,19 +180,19 @@ class HelloCubeApp final // NOSONAR - destructor required // Create index buffer for cube mesh m_index_buffer = GetRenderContext().CreateBuffer(Rhi::BufferSettings::ForIndexBuffer(m_cube_mesh.GetIndexDataSize(), GetIndexFormat(m_cube_mesh.GetIndex(0)))); m_index_buffer.SetName("Cube Index Buffer"); - m_index_buffer.SetData( - { { reinterpret_cast(m_cube_mesh.GetIndices().data()), m_cube_mesh.GetIndexDataSize() } }, // NOSONAR - m_render_cmd_queue - ); + m_index_buffer.SetData(m_render_cmd_queue, { + reinterpret_cast(m_cube_mesh.GetIndices().data()), // NOSONAR + m_cube_mesh.GetIndexDataSize() + }); #ifdef UNIFORMS_BUFFER_ENABLED // Create constant vertex buffer Rhi::Buffer vertex_buffer = GetRenderContext().CreateBuffer(Rhi::BufferSettings::ForVertexBuffer(m_cube_mesh.GetVertexDataSize(), m_cube_mesh.GetVertexSize())); vertex_buffer.SetName("Cube Vertex Buffer"); - vertex_buffer.SetData( - { { reinterpret_cast(m_cube_mesh.GetVertices().data()), m_cube_mesh.GetVertexDataSize() } }, // NOSONAR - m_render_cmd_queue - ); + vertex_buffer.SetData(m_render_cmd_queue, { + reinterpret_cast(m_cube_mesh.GetVertices().data()), // NOSONAR + m_cube_mesh.GetVertexDataSize() + }); m_vertex_buffer_set = Rhi::BufferSet(Rhi::BufferType::Vertex, { vertex_buffer }); const auto uniforms_data_size = static_cast(sizeof(m_shader_uniforms)); @@ -268,13 +269,13 @@ class HelloCubeApp final // NOSONAR - destructor required #ifdef UNIFORMS_BUFFER_ENABLED // Update uniforms buffer on GPU and apply model-view-projection transformation in vertex shader on GPU - frame.uniforms_buffer.SetData(m_shader_uniforms_subresources, m_render_cmd_queue); + frame.uniforms_buffer.SetData(m_render_cmd_queue, m_shader_uniforms_subresource); #else // Update vertex buffer with vertices in camera's projection view - frame.vertex_buffer_set[0].SetData( - { { reinterpret_cast(m_proj_vertices.data()), m_cube_mesh.GetVertexDataSize() } }, // NOSONAR - m_render_cmd_queue - ); + frame.vertex_buffer_set[0].SetData(m_render_cmd_queue, { + reinterpret_cast(m_proj_vertices.data()), // NOSONAR + m_cube_mesh.GetVertexDataSize() + }); #endif // Issue commands for cube rendering diff --git a/Apps/02-HelloCube/README.md b/Apps/02-HelloCube/README.md index 77a092823..3a77b095d 100644 --- a/Apps/02-HelloCube/README.md +++ b/Apps/02-HelloCube/README.md @@ -203,10 +203,10 @@ class HelloTriangleApp final : public GraphicsApp // Create index buffer for cube mesh m_index_buffer = GetRenderContext().CreateBuffer(Rhi::BufferSettings::ForIndexBuffer(m_cube_mesh.GetIndexDataSize(), GetIndexFormat(m_cube_mesh.GetIndex(0)))); - m_index_buffer.SetData( - { { reinterpret_cast(m_cube_mesh.GetIndices().data()), m_cube_mesh.GetIndexDataSize() } }, - m_render_cmd_queue - ); + m_index_buffer.SetData(m_render_cmd_queue, { + reinterpret_cast(m_cube_mesh.GetIndices().data()), + m_cube_mesh.GetIndexDataSize() + }); // Create per-frame command lists for(HelloCubeFrame& frame : GetFrames()) @@ -290,10 +290,10 @@ class HelloCubeApp final : public GraphicsApp const HelloCubeFrame& frame = GetCurrentFrame(); // Update vertex buffer with vertices in camera's projection view - frame.vertex_buffer_set[0].SetData( - { { reinterpret_cast(m_proj_vertices.data()), m_cube_mesh.GetVertexDataSize() } }, - m_render_cmd_queue - ); + frame.vertex_buffer_set[0].SetData(m_render_cmd_queue, { + reinterpret_cast(m_proj_vertices.data()), + m_cube_mesh.GetVertexDataSize() + }); // Issue commands for cube rendering META_DEBUG_GROUP_VAR(s_debug_group, "Cube Rendering"); @@ -544,10 +544,10 @@ class HelloCubeApp final : public GraphicsApp // Create constant vertex buffer Rhi::Buffer vertex_buffer = GetRenderContext().CreateBuffer(Rhi::BufferSettings::ForVertexBuffer(m_cube_mesh.GetVertexDataSize(), m_cube_mesh.GetVertexSize())); - vertex_buffer.SetData( - { { reinterpret_cast(m_cube_mesh.GetVertices().data()), m_cube_mesh.GetVertexDataSize() } }, - m_render_cmd_queue - ); + vertex_buffer.SetData(m_render_cmd_queue, { + reinterpret_cast(m_cube_mesh.GetVertices().data()), + m_cube_mesh.GetVertexDataSize() + }); m_vertex_buffer_set = Rhi::BufferSet(Rhi::BufferType::Vertex, { vertex_buffer }); const auto uniforms_data_size = static_cast(sizeof(m_shader_uniforms)); @@ -601,7 +601,7 @@ class HelloCubeApp final : public GraphicsApp const HelloCubeFrame& frame = GetCurrentFrame(); // Update uniforms buffer on GPU and apply model-view-projection transformation in vertex shader on GPU - frame.uniforms_buffer.SetData(m_shader_uniforms_subresources, m_render_cmd_queue); + frame.uniforms_buffer.SetData(m_render_cmd_queue, m_shader_uniforms_subresources); // Issue commands for cube rendering META_DEBUG_GROUP_VAR(s_debug_group, "Cube Rendering"); diff --git a/Apps/03-TexturedCube/README.md b/Apps/03-TexturedCube/README.md index d7a1ca716..d13572946 100644 --- a/Apps/03-TexturedCube/README.md +++ b/Apps/03-TexturedCube/README.md @@ -232,28 +232,28 @@ void TexturedCubeApp::Init() const Data::Size vertex_data_size = cube_mesh.GetVertexDataSize(); const Data::Size vertex_size = cube_mesh.GetVertexSize(); rhi::Buffer vertex_buffer = GetRenderContext().CreateBuffer(rhi::BufferSettings::ForVertexBuffer(vertex_data_size, vertex_size)); - vertex_buffer.SetData( - { { reinterpret_cast(cube_mesh.GetVertices().data()), vertex_data_size } }, - render_cmd_queue - ); + vertex_buffer.SetData(render_cmd_queue, { + reinterpret_cast(cube_mesh.GetVertices().data()), + vertex_data_size + }); m_vertex_buffer_set = rhi::BufferSet(rhi::BufferType::Vertex, { vertex_buffer }); // Create index buffer for cube mesh const Data::Size index_data_size = cube_mesh.GetIndexDataSize(); const gfx::PixelFormat index_format = gfx::GetIndexFormat(cube_mesh.GetIndex(0)); m_index_buffer = GetRenderContext().CreateBuffer(rhi::BufferSettings::ForIndexBuffer(index_data_size, index_format)); - m_index_buffer.SetData( - { { reinterpret_cast(cube_mesh.GetIndices().data()), index_data_size } }, - render_cmd_queue - ); + m_index_buffer.SetData(render_cmd_queue, { + reinterpret_cast(cube_mesh.GetIndices().data()), + index_data_size + }); // Create constants buffer for frame rendering const auto constants_data_size = static_cast(sizeof(m_shader_constants)); m_const_buffer = GetRenderContext().CreateBuffer(rhi::BufferSettings::ForConstantBuffer(constants_data_size)); - m_const_buffer.SetData( - { { reinterpret_cast(&m_shader_constants), constants_data_size } }, - render_cmd_queue - ); + m_const_buffer.SetData(render_cmd_queue, { + reinterpret_cast(&m_shader_constants), + constants_data_size + }); ... } @@ -449,7 +449,7 @@ bool TexturedCubeApp::Render() // Update uniforms buffer related to current frame const TexturedCubeFrame& frame = GetCurrentFrame(); const rhi::CommandQueue& render_cmd_queue = GetRenderContext().GetRenderCommandKit().GetQueue(); - frame.uniforms_buffer.SetData(m_shader_uniforms_subresources, render_cmd_queue); + frame.uniforms_buffer.SetData(render_cmd_queue, m_shader_uniforms_subresources); // Issue commands for cube rendering META_DEBUG_GROUP_VAR(s_debug_group, "Cube Rendering"); diff --git a/Apps/03-TexturedCube/TexturedCubeApp.cpp b/Apps/03-TexturedCube/TexturedCubeApp.cpp index 2539a48d8..8447124bf 100644 --- a/Apps/03-TexturedCube/TexturedCubeApp.cpp +++ b/Apps/03-TexturedCube/TexturedCubeApp.cpp @@ -78,10 +78,10 @@ void TexturedCubeApp::Init() const Data::Size vertex_size = cube_mesh.GetVertexSize(); rhi::Buffer vertex_buffer = GetRenderContext().CreateBuffer(rhi::BufferSettings::ForVertexBuffer(vertex_data_size, vertex_size)); vertex_buffer.SetName("Cube Vertex Buffer"); - vertex_buffer.SetData( - { { reinterpret_cast(cube_mesh.GetVertices().data()), vertex_data_size } }, // NOSONAR - render_cmd_queue - ); + vertex_buffer.SetData(render_cmd_queue, { + reinterpret_cast(cube_mesh.GetVertices().data()), // NOSONAR + vertex_data_size + }); m_vertex_buffer_set = rhi::BufferSet(rhi::BufferType::Vertex, { vertex_buffer }); // Create index buffer for cube mesh @@ -89,19 +89,19 @@ void TexturedCubeApp::Init() const gfx::PixelFormat index_format = gfx::GetIndexFormat(cube_mesh.GetIndex(0)); m_index_buffer = GetRenderContext().CreateBuffer(rhi::BufferSettings::ForIndexBuffer(index_data_size, index_format)); m_index_buffer.SetName("Cube Index Buffer"); - m_index_buffer.SetData( - { { reinterpret_cast(cube_mesh.GetIndices().data()), index_data_size } }, // NOSONAR - render_cmd_queue - ); + m_index_buffer.SetData(render_cmd_queue, { + reinterpret_cast(cube_mesh.GetIndices().data()), // NOSONAR + index_data_size + }); // Create constants buffer for frame rendering const auto constants_data_size = static_cast(sizeof(m_shader_constants)); m_const_buffer = GetRenderContext().CreateBuffer(rhi::BufferSettings::ForConstantBuffer(constants_data_size)); m_const_buffer.SetName("Constants Buffer"); - m_const_buffer.SetData( - { { reinterpret_cast(&m_shader_constants), constants_data_size } }, // NOSONAR - render_cmd_queue - ); + m_const_buffer.SetData(render_cmd_queue, { + reinterpret_cast(&m_shader_constants), // NOSONAR + constants_data_size + }); // Create render state with program m_render_state = GetRenderContext().CreateRenderState( @@ -216,7 +216,7 @@ bool TexturedCubeApp::Render() // Update uniforms buffer related to current frame const TexturedCubeFrame& frame = GetCurrentFrame(); const rhi::CommandQueue& render_cmd_queue = GetRenderContext().GetRenderCommandKit().GetQueue(); - frame.uniforms_buffer.SetData(m_shader_uniforms_subresources, render_cmd_queue); + frame.uniforms_buffer.SetData(render_cmd_queue, m_shader_uniforms_subresource); // Issue commands for cube rendering META_DEBUG_GROUP_VAR(s_debug_group, "Cube Rendering"); diff --git a/Apps/03-TexturedCube/TexturedCubeApp.h b/Apps/03-TexturedCube/TexturedCubeApp.h index b12f2b238..04984e437 100644 --- a/Apps/03-TexturedCube/TexturedCubeApp.h +++ b/Apps/03-TexturedCube/TexturedCubeApp.h @@ -87,8 +87,9 @@ class TexturedCubeApp final // NOSONAR - destructor required rhi::Texture m_cube_texture; rhi::Sampler m_texture_sampler; - const rhi::SubResources m_shader_uniforms_subresources{ - { reinterpret_cast(&m_shader_uniforms), sizeof(hlslpp::Uniforms) } // NOSONAR + const rhi::SubResource m_shader_uniforms_subresource{ + reinterpret_cast(&m_shader_uniforms), // NOSONAR + sizeof(hlslpp::Uniforms) }; }; diff --git a/Apps/04-ShadowCube/README.md b/Apps/04-ShadowCube/README.md index 2c9a3b494..0ec31c1fd 100644 --- a/Apps/04-ShadowCube/README.md +++ b/Apps/04-ShadowCube/README.md @@ -67,7 +67,7 @@ struct MeshUniforms `TexturedMeshBuffers` which is managing vertex, index, uniforms buffers and texture with data for particular mesh drawing passed to constructor as a reference to [BaseMesh]((../../../Modules/Graphics/Primitives/Include/Methane/Graphics/Mesh/BaseMesh.hpp)) object. -Supplementary member `m_scene_uniforms_subresources` stores a pointer to the `m_scene_uniforms` in the `std::vector` +Supplementary member `m_scene_uniforms_subresource` stores a pointer to the `m_scene_uniforms` in the `std::vector` type `gfx::IResource::SubResources` which is passed to `Rhi::Buffer::SetData(...)` method to update the buffer data on GPU. Two `gfx::Camera` objects are used: one `m_view_camera` is usual perspective view camera, while the other `m_light_camera` @@ -149,7 +149,7 @@ private: 30.F // - light_specular_factor }; hlslpp::SceneUniforms m_scene_uniforms{ }; - rhi::SubResources m_scene_uniforms_subresources{ + rhi::SubResources m_scene_uniforms_subresource{ { reinterpret_cast(&m_scene_uniforms), sizeof(hlslpp::SceneUniforms) } // NOSONAR }; gfx::Camera m_view_camera; @@ -308,7 +308,7 @@ Vertex shader since it will be used for rendering to depth buffer only without c final_state_settings.program.GetSettings().input_buffer_layouts, rhi::ProgramArgumentAccessors { - { { rhi::ShaderType::All, "g_mesh_uniforms" }, rhi::ProgramArgumentAccessor::Type::Mutable }, + { { rhi::ShaderType::Vertex, "g_mesh_uniforms" }, rhi::ProgramArgumentAccessor::Type::Mutable }, }, m_shadow_pass_pattern.GetAttachmentFormats() } @@ -385,12 +385,12 @@ rendered depth texture content for the next render pass. Render command list is // Shadow-pass resource bindings for cube rendering frame.shadow_pass.cube.program_bindings = shadow_state_settings.program.CreateBindings({ - { { rhi::ShaderType::All, "g_mesh_uniforms" }, { { frame.shadow_pass.cube.uniforms_buffer.GetInterface() } } }, + { { rhi::ShaderType::Vertex, "g_mesh_uniforms" }, { { frame.shadow_pass.cube.uniforms_buffer.GetInterface() } } }, }, frame.index); // Shadow-pass resource bindings for floor rendering frame.shadow_pass.floor.program_bindings = shadow_state_settings.program.CreateBindings({ - { { rhi::ShaderType::All, "g_mesh_uniforms" }, { { frame.shadow_pass.floor.uniforms_buffer.GetInterface() } } }, + { { rhi::ShaderType::Vertex, "g_mesh_uniforms" }, { { frame.shadow_pass.floor.uniforms_buffer.GetInterface() } } }, }, frame.index); // Create depth texture for shadow map rendering @@ -577,11 +577,11 @@ bool ShadowCubeApp::Render() // Upload uniform buffers to GPU const ShadowCubeFrame& frame = GetCurrentFrame(); const rhi::CommandQueue render_cmd_queue = GetRenderContext().GetRenderCommandKit().GetQueue(); - frame.scene_uniforms_buffer.SetData(m_scene_uniforms_subresources, render_cmd_queue); - frame.shadow_pass.floor.uniforms_buffer.SetData(m_floor_buffers_ptr->GetShadowPassUniformsSubresources(), render_cmd_queue); - frame.shadow_pass.cube.uniforms_buffer.SetData(m_cube_buffers_ptr->GetShadowPassUniformsSubresources(), render_cmd_queue); - frame.final_pass.floor.uniforms_buffer.SetData(m_floor_buffers_ptr->GetFinalPassUniformsSubresources(), render_cmd_queue); - frame.final_pass.cube.uniforms_buffer.SetData(m_cube_buffers_ptr->GetFinalPassUniformsSubresources(), render_cmd_queue); + frame.scene_uniforms_buffer.SetData(render_cmd_queue, m_scene_uniforms_subresource); + frame.shadow_pass.floor.uniforms_buffer.SetData(render_cmd_queue, m_floor_buffers_ptr->GetShadowPassUniformsSubresources()); + frame.shadow_pass.cube.uniforms_buffer.SetData(render_cmd_queue, m_cube_buffers_ptr->GetShadowPassUniformsSubresources()); + frame.final_pass.floor.uniforms_buffer.SetData(render_cmd_queue, m_floor_buffers_ptr->GetFinalPassUniformsSubresources()); + frame.final_pass.cube.uniforms_buffer.SetData(render_cmd_queue, m_cube_buffers_ptr->GetFinalPassUniformsSubresources()); // Record commands for shadow & final render passes RenderScene(m_shadow_pass, frame.shadow_pass); diff --git a/Apps/04-ShadowCube/ShadowCubeApp.cpp b/Apps/04-ShadowCube/ShadowCubeApp.cpp index 9968ddf51..9b8d33424 100644 --- a/Apps/04-ShadowCube/ShadowCubeApp.cpp +++ b/Apps/04-ShadowCube/ShadowCubeApp.cpp @@ -97,10 +97,10 @@ void ShadowCubeApp::Init() // Create constants buffer for frame rendering m_const_buffer = render_context.CreateBuffer(rhi::BufferSettings::ForConstantBuffer(constants_data_size)); m_const_buffer.SetName("Constants Buffer"); - m_const_buffer.SetData( - { { reinterpret_cast(&m_scene_constants), sizeof(m_scene_constants) } }, // NOSONAR - render_cmd_queue - ); + m_const_buffer.SetData(render_cmd_queue, { + reinterpret_cast(&m_scene_constants), // NOSONAR + sizeof(m_scene_constants) + }); // Create sampler for cube and floor textures sampling m_texture_sampler = render_context.CreateSampler( @@ -198,7 +198,7 @@ void ShadowCubeApp::Init() final_state_settings.program.GetSettings().input_buffer_layouts, rhi::ProgramArgumentAccessors { - { { rhi::ShaderType::All, "g_mesh_uniforms" }, rhi::ProgramArgumentAccessor::Type::Mutable }, + { { rhi::ShaderType::Vertex, "g_mesh_uniforms" }, rhi::ProgramArgumentAccessor::Type::Mutable }, }, m_shadow_pass_pattern.GetAttachmentFormats() } @@ -241,13 +241,13 @@ void ShadowCubeApp::Init() // Shadow-pass resource bindings for cube rendering frame.shadow_pass.cube.program_bindings = shadow_state_settings.program.CreateBindings({ - { { rhi::ShaderType::All, "g_mesh_uniforms" }, { { frame.shadow_pass.cube.uniforms_buffer.GetInterface() } } }, + { { rhi::ShaderType::Vertex, "g_mesh_uniforms" }, { { frame.shadow_pass.cube.uniforms_buffer.GetInterface() } } }, }, frame.index); frame.shadow_pass.cube.program_bindings.SetName(IndexedName("Cube Shadow-Pass Bindings {}", frame.index)); // Shadow-pass resource bindings for floor rendering frame.shadow_pass.floor.program_bindings = shadow_state_settings.program.CreateBindings({ - { { rhi::ShaderType::All, "g_mesh_uniforms" }, { { frame.shadow_pass.floor.uniforms_buffer.GetInterface() } } }, + { { rhi::ShaderType::Vertex, "g_mesh_uniforms" }, { { frame.shadow_pass.floor.uniforms_buffer.GetInterface() } } }, }, frame.index); frame.shadow_pass.floor.program_bindings.SetName(IndexedName("Floor Shadow-Pass Bindings {}", frame.index)); @@ -389,11 +389,11 @@ bool ShadowCubeApp::Render() // Upload uniform buffers to GPU const ShadowCubeFrame& frame = GetCurrentFrame(); const rhi::CommandQueue render_cmd_queue = GetRenderContext().GetRenderCommandKit().GetQueue(); - frame.scene_uniforms_buffer.SetData(m_scene_uniforms_subresources, render_cmd_queue); - frame.shadow_pass.floor.uniforms_buffer.SetData(m_floor_buffers_ptr->GetShadowPassUniformsSubresources(), render_cmd_queue); - frame.shadow_pass.cube.uniforms_buffer.SetData(m_cube_buffers_ptr->GetShadowPassUniformsSubresources(), render_cmd_queue); - frame.final_pass.floor.uniforms_buffer.SetData(m_floor_buffers_ptr->GetFinalPassUniformsSubresources(), render_cmd_queue); - frame.final_pass.cube.uniforms_buffer.SetData(m_cube_buffers_ptr->GetFinalPassUniformsSubresources(), render_cmd_queue); + frame.scene_uniforms_buffer.SetData(render_cmd_queue, m_scene_uniforms_subresource); + frame.shadow_pass.floor.uniforms_buffer.SetData(render_cmd_queue, m_floor_buffers_ptr->GetShadowPassUniformsSubresource()); + frame.shadow_pass.cube.uniforms_buffer.SetData(render_cmd_queue, m_cube_buffers_ptr->GetShadowPassUniformsSubresource()); + frame.final_pass.floor.uniforms_buffer.SetData(render_cmd_queue, m_floor_buffers_ptr->GetFinalPassUniformsSubresource()); + frame.final_pass.cube.uniforms_buffer.SetData(render_cmd_queue, m_cube_buffers_ptr->GetFinalPassUniformsSubresource()); // Record commands for shadow & final render passes RenderScene(m_shadow_pass, frame.shadow_pass); diff --git a/Apps/04-ShadowCube/ShadowCubeApp.h b/Apps/04-ShadowCube/ShadowCubeApp.h index 3dd0e47af..93ba5dceb 100644 --- a/Apps/04-ShadowCube/ShadowCubeApp.h +++ b/Apps/04-ShadowCube/ShadowCubeApp.h @@ -94,13 +94,14 @@ class ShadowCubeApp final // NOSONAR - destructor required void SetShadowPassUniforms(hlslpp::MeshUniforms&& uniforms) noexcept { m_shadow_pass_uniforms = std::move(uniforms); } - [[nodiscard]] const hlslpp::MeshUniforms& GetShadowPassUniforms() const noexcept { return m_shadow_pass_uniforms; } - [[nodiscard]] const rhi::SubResources& GetShadowPassUniformsSubresources() const noexcept { return m_shadow_pass_uniforms_subresources; } + [[nodiscard]] const hlslpp::MeshUniforms& GetShadowPassUniforms() const noexcept { return m_shadow_pass_uniforms; } + [[nodiscard]] const rhi::SubResource& GetShadowPassUniformsSubresource() const noexcept { return m_shadow_pass_uniforms_subresource; } private: - hlslpp::MeshUniforms m_shadow_pass_uniforms{}; - rhi::SubResources m_shadow_pass_uniforms_subresources{ - { reinterpret_cast(&m_shadow_pass_uniforms), sizeof(hlslpp::MeshUniforms) } // NOSONAR + hlslpp::MeshUniforms m_shadow_pass_uniforms{}; + const rhi::SubResource m_shadow_pass_uniforms_subresource{ + reinterpret_cast(&m_shadow_pass_uniforms), // NOSONAR + sizeof(hlslpp::MeshUniforms) }; }; @@ -126,8 +127,9 @@ class ShadowCubeApp final // NOSONAR - destructor required 30.F // - light_specular_factor }; hlslpp::SceneUniforms m_scene_uniforms{ }; - rhi::SubResources m_scene_uniforms_subresources{ - { reinterpret_cast(&m_scene_uniforms), sizeof(hlslpp::SceneUniforms) } // NOSONAR + rhi::SubResource m_scene_uniforms_subresource{ + reinterpret_cast(&m_scene_uniforms), // NOSONAR + sizeof(hlslpp::SceneUniforms) }; gfx::Camera m_view_camera; gfx::Camera m_light_camera; diff --git a/Apps/06-CubeMapArray/CubeMapArrayApp.cpp b/Apps/06-CubeMapArray/CubeMapArrayApp.cpp index 54d9f6d7d..f5f814eb8 100644 --- a/Apps/06-CubeMapArray/CubeMapArrayApp.cpp +++ b/Apps/06-CubeMapArray/CubeMapArrayApp.cpp @@ -262,7 +262,7 @@ bool CubeMapArrayApp::Render() // Update uniforms buffer related to current frame const CubeMapArrayFrame& frame = GetCurrentFrame(); const rhi::CommandQueue render_cmd_queue = GetRenderContext().GetRenderCommandKit().GetQueue(); - frame.cube.uniforms_buffer.SetData(m_cube_buffers_ptr->GetFinalPassUniformsSubresources(), render_cmd_queue); + frame.cube.uniforms_buffer.SetData(render_cmd_queue, m_cube_buffers_ptr->GetFinalPassUniformsSubresource()); // 1) Render cube instances of 'CUBE_MAP_ARRAY_SIZE' count META_DEBUG_GROUP_VAR(s_debug_group, "Cube Instances Rendering"); diff --git a/Apps/07-ParallelRendering/ParallelRenderingApp.cpp b/Apps/07-ParallelRendering/ParallelRenderingApp.cpp index ab838e01e..8285b9b23 100644 --- a/Apps/07-ParallelRendering/ParallelRenderingApp.cpp +++ b/Apps/07-ParallelRendering/ParallelRenderingApp.cpp @@ -388,7 +388,7 @@ bool ParallelRenderingApp::Render() // Update uniforms buffer related to current frame const ParallelRenderingFrame& frame = GetCurrentFrame(); const rhi::CommandQueue render_cmd_queue = GetRenderContext().GetRenderCommandKit().GetQueue(); - frame.cubes_array.uniforms_buffer.SetData(m_cube_array_buffers_ptr->GetFinalPassUniformsSubresources(), render_cmd_queue); + frame.cubes_array.uniforms_buffer.SetData(render_cmd_queue, m_cube_array_buffers_ptr->GetFinalPassUniformsSubresource()); // Render cube instances of 'CUBE_MAP_ARRAY_SIZE' count if (m_settings.parallel_rendering_enabled) diff --git a/Apps/07-ParallelRendering/README.md b/Apps/07-ParallelRendering/README.md index 2985602b4..f57c98945 100644 --- a/Apps/07-ParallelRendering/README.md +++ b/Apps/07-ParallelRendering/README.md @@ -16,7 +16,7 @@ Tutorial demonstrates the following techniques: - Creating render target texture 2D array; - Rendering text labels to the faces of texture 2D array via separate render passes using helper class [TextureLabeler](/Apps/Common/Include/TextureLabeler.h); - - Using [MeshBuffers](Modules/Graphics/Extensions/Include/Methane/Graphics/MeshBuffers.hpp) extension + - Using [MeshBuffers](/Modules/Graphics/Extensions/Include/Methane/Graphics/MeshBuffers.hpp) extension primitive to represent cube instances, create index and vertex buffers and render with a single Draw command. - Using single addressable uniforms buffer to store an array of uniform structures for all cube instance parameters at once and binding array elements in that buffer to the particular @@ -43,4 +43,9 @@ derived from [Platform::Input::Keyboard::ActionControllerBase](/Modules/Platform Common keyboard controls are enabled by the `Platform`, `Graphics` and `UserInterface` application controllers: - [Methane::Platform::AppController](/Modules/Platform/App/README.md#platform-application-controller) - [Methane::Graphics::AppController, AppContextController](/Modules/Graphics/App/README.md#graphics-application-controllers) -- [Methane::UserInterface::AppController](/Modules/UserInterface/App/README.md#user-interface-application-controllers) \ No newline at end of file +- [Methane::UserInterface::AppController](/Modules/UserInterface/App/README.md#user-interface-application-controllers) + +## Continue learning + +Continue learning Methane Graphics programming in the next tutorial [ConsoleCompute](../08-ConsoleCompute), +which is demonstrating computing on GPU in pure console application. diff --git a/Apps/08-ConsoleCompute/CMakeLists.txt b/Apps/08-ConsoleCompute/CMakeLists.txt new file mode 100644 index 000000000..b6b98eee4 --- /dev/null +++ b/Apps/08-ConsoleCompute/CMakeLists.txt @@ -0,0 +1,66 @@ +if(APPLE_IOS OR APPLE_TVOS) + # Console applications are not supported on iOS / tvOS + return() +endif() + +set(TARGET MethaneConsoleCompute) + +include(MethaneShaders) + +add_executable(${TARGET} + ConsoleApp.h + ConsoleApp.cpp + ConsoleComputeApp.h + ConsoleComputeApp.cpp +) + +target_link_libraries(${TARGET} + PRIVATE + MethaneKit + ftxui::component # FTXUI +) + +add_methane_shaders_source( + TARGET ${TARGET} + SOURCE Shaders/GameOfLife.hlsl + VERSION 6_0 + TYPES + comp=MainCS +) + +add_methane_shaders_library(${TARGET}) + +set_target_properties(${TARGET} + PROPERTIES + FOLDER Apps +) + +install( + TARGETS ${TARGET} + RUNTIME + DESTINATION Apps + COMPONENT Runtime +) + +# PREREQUISITE_RESOURCES contains compiled Metal shaders library, which should be copied next to executable +get_target_property(COPY_RESOURCES ${TARGET} PREREQUISITE_RESOURCES) +if (COPY_RESOURCES AND NOT COPY_RESOURCES STREQUAL "COPY_RESOURCES-NOTFOUND") + + if(METHANE_GFX_API EQUAL METHANE_GFX_METAL) + get_target_property(SHADER_TARGET ${TARGET} METAL_LIB_TARGET) + else() + set(SHADER_TARGETS ${TARGET}) + endif() + + add_custom_command(TARGET ${SHADER_TARGET} POST_BUILD + COMMENT "Copying prerequisite resources for application ${TARGET}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${COPY_RESOURCES} "$" + ) + + install( + FILES ${COPY_RESOURCES} + DESTINATION Apps + COMPONENT Runtime + ) + +endif() \ No newline at end of file diff --git a/Apps/08-ConsoleCompute/ConsoleApp.cpp b/Apps/08-ConsoleCompute/ConsoleApp.cpp new file mode 100644 index 000000000..bc9858d7b --- /dev/null +++ b/Apps/08-ConsoleCompute/ConsoleApp.cpp @@ -0,0 +1,207 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: ConsoleApp.cpp +Console UI application base class implemented using FTXUI framework + +******************************************************************************/ + +#include "ConsoleApp.h" + +#include +#include + +#include +#include + +namespace Methane::Tutorials +{ + +int ConsoleApp::Run() +{ + META_FUNCTION_TASK(); + std::atomic refresh_ui_continue = true; + std::thread refresh_ui([this, &refresh_ui_continue] + { + uint32_t time = 0; + std::condition_variable_any update_condition_var; + while (refresh_ui_continue) + { + using namespace std::chrono_literals; + std::this_thread::sleep_for(m_30fps_screen_refresh_limit_enabled ? 32ms : 1ms); + std::unique_lock lock(m_screen_refresh_mutex); + update_condition_var.wait_for(lock, 1s, [this] { return m_screen_refresh_enabled.load(); }); + m_screen.Post([&time] { time++; }); + m_screen.Post(ftxui::Event::Custom); + } + }); + + m_screen.Loop(m_root); + refresh_ui_continue = false; + refresh_ui.join(); + return 0; +} + +void ConsoleApp::ToggleScreenRefresh() +{ + m_screen_refresh_enabled = !m_screen_refresh_enabled; +} + +void ConsoleApp::InitUserInterface() +{ + META_FUNCTION_TASK(); + using namespace ftxui; + auto toolbar = Container::Horizontal({ + Renderer([this] + { + return hbox({ + text(fmt::format(" API: {} ", GetGraphicsApiName())), + separator(), + text(fmt::format(" GPU: {} ", GetComputeDeviceName())), + separator(), + text(fmt::format(" FPS: {} ", GetFramesCountPerSecond())), + separator(), + text(fmt::format(" Field: {} x {} ", m_field_size.GetWidth(), m_field_size.GetHeight())), + separator(), + text(fmt::format(" Visible {} ", static_cast(m_frame_rect) )), + separator(), + text(fmt::format(" Visible Cells {} ", GetVisibleCellsCount())) + }); + }) | border | xflex, + Button(" X ", m_screen.ExitLoopClosure(), ButtonOption::Simple()) | align_right + }); + + m_compute_device_option.on_change = [this]() + { + Release(); + Init(); + }; + + auto sidebar = Container::Vertical({ + Renderer([]{ return text("GPU Devices:") | ftxui::bold; }), + Radiobox(&GetComputeDeviceNames(), &m_compute_device_index, m_compute_device_option), + Renderer([] { return separator(); }), + Checkbox("30 FPS limit", &m_30fps_screen_refresh_limit_enabled), + Container::Horizontal({ + Button("Restart", [this]() { Restart(); }, ButtonOption::Border()), + Button("Play | Pause", [this]() { ToggleScreenRefresh(); }, ButtonOption::Border()), + Button("Next Step", [this]() { Compute(); }, ButtonOption::Border()), + }), + Slider("Initial Cells %", &m_initial_cells_percent), + Renderer([] + { + return vbox({ + separator(), + paragraph("Controls:") | ftxui::bold, + paragraph(" ◆ Press mouse left button over game field to drag the visible area."), + paragraph(" ◆ Press mouse left button over vertical splitter and drag to resize left panel."), + paragraph(" ◆ Resize console window to increase canvas area."), + separator(), + paragraph("Conway's Game of Life Rules:") | ftxui::bold, + paragraph(" ◆ Any live cell with fewer than two live neighbours dies, as if by underpopulation."), + paragraph(" ◆ Any live cell with two or three live neighbours lives on to the next generation."), + paragraph(" ◆ Any live cell with more than three live neighbours dies, as if by overpopulation."), + paragraph(" ◆ Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction."), + vbox() | yflex, + separator(), + paragraph(fmt::format("Powered by {} v{} {}", METHANE_PRODUCT_NAME, METHANE_VERSION_STR, METHANE_PRODUCT_URL)) + }) | yflex; + }) | yflex + }); + + auto canvas = Renderer([this] + { + return ftxui::canvas([this](Canvas& c) + { + UpdateFrameSize(c.width(), c.height()); + if (m_screen_refresh_enabled) + { + Compute(); + } + Present(c); + }) | flex; + }); + + auto canvas_with_mouse = CatchEvent(canvas, [this](Event e) // NOSONAR + { + return HandleInputEvent(e); + }); + + static int s_sidebar_width = 35; + auto main_container = Container::Vertical( + { + toolbar | xflex, + ResizableSplitLeft(sidebar, canvas_with_mouse, &s_sidebar_width) | border | flex + }); + + m_root = Renderer(main_container, [main_container] + { + return vbox({ + text("Methane Console Compute: Game of Life") | ftxui::bold | hcenter, + main_container->Render() | flex, + }); + }); +} + +void ConsoleApp::UpdateFrameSize(int width, int height) +{ + META_FUNCTION_TASK(); + if (!static_cast(m_frame_rect.size)) + { + // Set initial frame position in the center of game field + m_frame_rect.origin.SetX((m_field_size.GetWidth() - width) / 2); + m_frame_rect.origin.SetY((m_field_size.GetHeight() - height) / 2); + } + + // Update frame size + m_frame_rect.size.SetWidth(width); + m_frame_rect.size.SetHeight(height); +} + +bool ConsoleApp::HandleInputEvent(ftxui::Event& e) +{ + META_FUNCTION_TASK(); + if (!e.is_mouse()) + return false; + + if (e.mouse().button == ftxui::Mouse::Button::Left) + { + const data::Point2I mouse_current_pos(e.mouse().x, e.mouse().y); + if (m_mouse_pressed_pos.has_value()) + { + const data::Point2I shift = (*m_mouse_pressed_pos - mouse_current_pos) * 2; + m_frame_rect.origin.SetX(std::max(0, std::min(m_frame_pressed_pos->GetX() + shift.GetX(), + static_cast(m_field_size.GetWidth() - m_frame_rect.size.GetWidth() - 1)))); + m_frame_rect.origin.SetY(std::max(0, std::min(m_frame_pressed_pos->GetY() + shift.GetY(), + static_cast(m_field_size.GetHeight() - m_frame_rect.size.GetHeight() - 1)))); + } + else + { + m_mouse_pressed_pos = mouse_current_pos; + m_frame_pressed_pos = m_frame_rect.origin; + } + } + else if (m_mouse_pressed_pos.has_value()) + { + m_mouse_pressed_pos.reset(); + m_frame_pressed_pos.reset(); + } + return false; +} + +} // namespace Methane::Tutorials diff --git a/Apps/08-ConsoleCompute/ConsoleApp.h b/Apps/08-ConsoleCompute/ConsoleApp.h new file mode 100644 index 000000000..14e02a261 --- /dev/null +++ b/Apps/08-ConsoleCompute/ConsoleApp.h @@ -0,0 +1,92 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: ConsoleApp.h +Console UI application base class implemented using FTXUI framework + +******************************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +#include +#include + +namespace Methane::Tutorials +{ + +namespace data = Methane::Data; + +class ConsoleApp +{ +public: + ConsoleApp() = default; + virtual ~ConsoleApp() = default; + + const data::FrameSize& GetFieldSize() const { return m_field_size; } + const data::FrameRect& GetVisibleFrameRect() const { return m_frame_rect; } + + double GetInitialCellsRatio() const { return static_cast(m_initial_cells_percent) / 100.0; } + bool IsScreenRefreshEnabled() const { return m_screen_refresh_enabled; } + void ToggleScreenRefresh(); + + // ConsoleApp virtual interface + virtual int Run(); + virtual std::string_view GetGraphicsApiName() const = 0; + virtual const std::string& GetComputeDeviceName() const = 0; + virtual const std::vector& GetComputeDeviceNames() const = 0; + virtual uint32_t GetFramesCountPerSecond() const = 0; + virtual uint32_t GetVisibleCellsCount() const = 0; + +protected: + virtual void Init() = 0; + virtual void Release() = 0; + virtual void Compute() = 0; + virtual void Present(ftxui::Canvas& canvas) = 0; + virtual void Restart() = 0; + + int GetComputeDeviceIndex() const { return m_compute_device_index; } + std::mutex& GetScreenRefreshMutex() { return m_screen_refresh_mutex; } + + void InitUserInterface(); + +private: + void UpdateFrameSize(int width, int height); + bool HandleInputEvent(ftxui::Event& e); + + ftxui::ScreenInteractive m_screen{ ftxui::ScreenInteractive::Fullscreen() }; + ftxui::RadioboxOption m_compute_device_option{ ftxui::RadioboxOption::Simple() }; + ftxui::Component m_root; + std::mutex m_screen_refresh_mutex; + std::atomic m_screen_refresh_enabled{ true }; + bool m_30fps_screen_refresh_limit_enabled{ true }; + int m_compute_device_index = 0; + data::FrameSize m_field_size{ 2048U, 2048U }; + data::FrameRect m_frame_rect; + std::optional m_mouse_pressed_pos; + std::optional m_frame_pressed_pos; + int m_initial_cells_percent = 50U; +}; + +} // namespace Methane::Tutorials diff --git a/Apps/08-ConsoleCompute/ConsoleComputeApp.cpp b/Apps/08-ConsoleCompute/ConsoleComputeApp.cpp new file mode 100644 index 000000000..d2d1478b2 --- /dev/null +++ b/Apps/08-ConsoleCompute/ConsoleComputeApp.cpp @@ -0,0 +1,276 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: ConsoleComputeApp.cpp +Tutorial demonstrating "game of life" computing on GPU in console application + +******************************************************************************/ + +#include "ConsoleComputeApp.h" + +#include +#include +#include + +#include +#include + +namespace gfx = Methane::Graphics; +namespace data = Methane::Data; + +namespace Methane::Tutorials +{ + +static const rhi::Devices& GetComputeDevices() +{ + META_FUNCTION_TASK(); + static const rhi::Devices& s_compute_devices = []() + { + rhi::System::Get().UpdateGpuDevices(rhi::DeviceCaps{ + rhi::DeviceFeatureMask{}, + 0U, // render_queues_count + 1U, // transfer_queues_count + 1U // compute_queues_count + }); + return rhi::System::Get().GetGpuDevices(); + }(); + return s_compute_devices; +} + +static Methane::Data::Bytes GetRandomFrameData(std::mt19937& random_engine, const gfx::FrameSize& frame_size, double initial_cells_ratio) +{ + META_FUNCTION_TASK(); + Methane::Data::Bytes frame_data(frame_size.GetPixelsCount(), std::byte()); + const auto cells_count = static_cast(static_cast(frame_size.GetPixelsCount()) * initial_cells_ratio); + std::uniform_int_distribution dist(0U, frame_size.GetPixelsCount() - 1U); + auto* cell_values = reinterpret_cast(frame_data.data()); // NOSONAR + for(uint32_t i = 0; i < cells_count; i++) + { + uint32_t p = 0U; + do + { + p = dist(random_engine); + } + while(cell_values[p]); + cell_values[p] = 1U; + } + return frame_data; +} + +ConsoleComputeApp::ConsoleComputeApp() + : m_random_engine([]() { // NOSONAR + std::random_device r; + std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()}; + return std::mt19937(seed); + }()) +{ + InitUserInterface(); + Init(); +} + +const rhi::Device* ConsoleComputeApp::GetComputeDevice() const +{ + META_FUNCTION_TASK(); + const int compute_device_index = GetComputeDeviceIndex(); + const rhi::Devices& devices = GetComputeDevices(); + return compute_device_index < static_cast(devices.size()) ? &devices[compute_device_index] : nullptr; +} + +int ConsoleComputeApp::Run() +{ + if (!GetComputeDevice()) + { + std::cerr << "ERROR: No GPU devices are available for computing!"; + return 1; + } + + return ConsoleApp::Run(); +} + +std::string_view ConsoleComputeApp::GetGraphicsApiName() const +{ + return magic_enum::enum_name(rhi::System::GetNativeApi()); +} + +const std::string& ConsoleComputeApp::GetComputeDeviceName() const +{ + META_FUNCTION_TASK(); + static const std::string s_no_adapter = "N/A"; + const rhi::Device* device_ptr = ConsoleComputeApp::GetComputeDevice(); + return device_ptr ? device_ptr->GetAdapterName() : s_no_adapter; +} + +const std::vector& ConsoleComputeApp::GetComputeDeviceNames() const +{ + META_FUNCTION_TASK(); + static const std::vector s_compute_device_names = []() + { + std::vector device_names; + for(const rhi::Device& device : GetComputeDevices()) + { + device_names.emplace_back(device.GetAdapterName()); + } + return device_names; + }(); + return s_compute_device_names; +} + +uint32_t ConsoleComputeApp::GetFramesCountPerSecond() const +{ + return IsScreenRefreshEnabled() ? m_fps_counter.GetFramesPerSecond() : 0U; +} + +uint32_t ConsoleComputeApp::GetVisibleCellsCount() const +{ + return m_visible_cells_count; +} + +void ConsoleComputeApp::Init() +{ + META_FUNCTION_TASK(); + const rhi::Device* device_ptr = GetComputeDevice(); + m_compute_context = device_ptr->CreateComputeContext(m_parallel_executor, {}); + m_compute_context.SetName("Game of Life"); + + m_compute_state = m_compute_context.CreateComputeState({ + m_compute_context.CreateProgram({ + rhi::Program::ShaderSet { { rhi::ShaderType::Compute, { data::ShaderProvider::Get(), { "GameOfLife", "MainCS" } } } }, + rhi::ProgramInputBufferLayouts { }, + rhi::ProgramArgumentAccessors + { + { { rhi::ShaderType::Compute, "g_frame_texture" }, rhi::ProgramArgumentAccessor::Type::Mutable }, + }, + }), + rhi::ThreadGroupSize(16U, 16U, 1U) + }); + m_compute_state.GetProgram().SetName("Game of Life Program"); + m_compute_state.SetName("Game of Life Compute State"); + + m_compute_cmd_list = m_compute_context.GetComputeCommandKit().GetQueue().CreateComputeCommandList(); + m_compute_cmd_list.SetName("Game of Life Compute"); + m_compute_cmd_list_set = rhi::CommandListSet({ m_compute_cmd_list.GetInterface() }); + + rhi::TextureSettings frame_texture_settings = rhi::TextureSettings::ForImage( + gfx::Dimensions(GetFieldSize()), + std::nullopt, gfx::PixelFormat::R8Uint, false, + rhi::ResourceUsageMask{ + rhi::ResourceUsage::ShaderRead, + rhi::ResourceUsage::ShaderWrite, + rhi::ResourceUsage::ReadBack + } + ); + m_frame_texture = m_compute_context.CreateTexture(frame_texture_settings); + m_frame_texture.SetName("Game of Life Frame Texture"); + + m_compute_bindings = m_compute_state.GetProgram().CreateBindings({ + { { rhi::ShaderType::Compute, "g_frame_texture" }, { { m_frame_texture.GetInterface() } } }, + }); + m_compute_bindings.SetName("Game of Life Compute Bindings"); + + RandomizeFrameData(); + + // Complete bindings and texture initialization + m_compute_context.CompleteInitialization(); +} + +void ConsoleComputeApp::Release() +{ + META_FUNCTION_TASK(); + m_compute_context.WaitForGpu(rhi::ContextWaitFor::ComputeComplete); + + m_compute_cmd_list_set = {}; + m_compute_cmd_list = {}; + m_compute_bindings = {}; + m_frame_texture = {}; + m_compute_state = {}; + m_compute_context = {}; +} + +void ConsoleComputeApp::Compute() +{ + META_FUNCTION_TASK(); + const data::FrameSize& field_size = GetFieldSize(); + const rhi::CommandQueue& compute_cmd_queue = m_compute_context.GetComputeCommandKit().GetQueue(); + const rhi::ThreadGroupSize& thread_group_size = m_compute_state.GetSettings().thread_group_size; + const rhi::ThreadGroupsCount thread_groups_count(data::DivCeil(field_size.GetWidth(), thread_group_size.GetWidth()), + data::DivCeil(field_size.GetHeight(), thread_group_size.GetHeight()), + 1U); + + META_DEBUG_GROUP_VAR(s_debum_group, "Compute Frame"); + m_compute_cmd_list.ResetWithState(m_compute_state, &s_debum_group); + m_compute_cmd_list.SetProgramBindings(m_compute_bindings); + m_compute_cmd_list.Dispatch(thread_groups_count); + m_compute_cmd_list.Commit(); + + compute_cmd_queue.Execute(m_compute_cmd_list_set); + m_compute_context.WaitForGpu(rhi::ContextWaitFor::ComputeComplete); + m_frame_data = m_frame_texture.GetData(compute_cmd_queue); + m_fps_counter.OnCpuFrameReadyToPresent(); +} + +void ConsoleComputeApp::Present(ftxui::Canvas& canvas) +{ + META_FUNCTION_TASK(); + const data::FrameSize& field_size = GetFieldSize(); + const data::FrameRect& frame_rect = GetVisibleFrameRect(); + const uint8_t* cells = m_frame_data.GetDataPtr(); + m_visible_cells_count = 0U; + + for (uint32_t y = 0; y < frame_rect.size.GetHeight(); y++) + { + const uint32_t cell_y = frame_rect.origin.GetY() + y; + const uint32_t cell_shift = cell_y * field_size.GetWidth(); + for (uint32_t x = 0; x < frame_rect.size.GetWidth(); x++) + { + const uint32_t cell_x = frame_rect.origin.GetX() + x; + if (cells[cell_shift + cell_x]) + { + canvas.DrawBlockOn(static_cast(x), static_cast(y)); + m_visible_cells_count++; + } + } + } + m_fps_counter.OnCpuFramePresented(); +} + +void ConsoleComputeApp::Restart() +{ + META_FUNCTION_TASK(); + std::unique_lock lock(GetScreenRefreshMutex()); + m_compute_context.WaitForGpu(rhi::ContextWaitFor::ComputeComplete); + RandomizeFrameData(); + m_compute_context.UploadResources(); +} + +void ConsoleComputeApp::RandomizeFrameData() +{ + META_FUNCTION_TASK(); + + // Randomize initial game state + m_frame_data = rhi::SubResource(GetRandomFrameData(m_random_engine, GetFieldSize(), GetInitialCellsRatio())); + + // Set frame texture data + m_frame_texture.SetData(m_compute_context.GetComputeCommandKit().GetQueue(), { m_frame_data }); +} + +} // namespace Methane::Tutorials + +int main(int, const char*[]) +{ + return Methane::Tutorials::ConsoleComputeApp().Run(); +} \ No newline at end of file diff --git a/Apps/08-ConsoleCompute/ConsoleComputeApp.h b/Apps/08-ConsoleCompute/ConsoleComputeApp.h new file mode 100644 index 000000000..f12e36a84 --- /dev/null +++ b/Apps/08-ConsoleCompute/ConsoleComputeApp.h @@ -0,0 +1,79 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: ConsoleComputeApp.h +Tutorial demonstrating "game of life" computing on GPU in console application + +******************************************************************************/ + +#pragma once + +#include "ConsoleApp.h" + +#include +#include + +#include +#include +#include + +namespace rhi = Methane::Graphics::Rhi; + +namespace Methane::Tutorials +{ + +class ConsoleComputeApp final + : public ConsoleApp +{ +public: + ConsoleComputeApp(); + + const rhi::Device* GetComputeDevice() const; + + // ConsoleApp overrides + int Run() override; + std::string_view GetGraphicsApiName() const override; + const std::string& GetComputeDeviceName() const override; + const std::vector& GetComputeDeviceNames() const override; + uint32_t GetFramesCountPerSecond() const override; + uint32_t GetVisibleCellsCount() const override; + +protected: + void Init() override; + void Release() override; + void Compute() override; + void Present(ftxui::Canvas& canvas) override; + void Restart() override; + +private: + void RandomizeFrameData(); + + std::mt19937 m_random_engine; + tf::Executor m_parallel_executor; + rhi::ComputeContext m_compute_context; + rhi::ComputeState m_compute_state; + rhi::ComputeCommandList m_compute_cmd_list; + rhi::CommandListSet m_compute_cmd_list_set; + rhi::Texture m_frame_texture; + rhi::ProgramBindings m_compute_bindings; + rhi::SubResource m_frame_data; + Data::FpsCounter m_fps_counter{ 60U }; + uint32_t m_visible_cells_count{ 0U }; +}; + +} // namespace Methane::Tutorials diff --git a/Apps/08-ConsoleCompute/README.md b/Apps/08-ConsoleCompute/README.md new file mode 100644 index 000000000..48e4ad183 --- /dev/null +++ b/Apps/08-ConsoleCompute/README.md @@ -0,0 +1,185 @@ +# Console Compute Tutorial + +|
Windows (DirectX 12)       
|
Linux (Vulkan)             
|
MacOS (Metal)              
| +|--------------------------------------------------------------------------|---------------------------------------------------------------------|--------------------------------------------------------------------| +| ![ConsoleCompute on Windows](Screenshots/ConsoleComputeWinDirectX12.jpg) | ![ConsoleCompute on Linux](Screenshots/ConsoleComputeLinVulkan.jpg) | ![ConsoleCompute on MacOS](Screenshots/ConsoleComputeMacMetal.jpg) | + +Tutorial implements [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) +in Compute shader running in pure Console application without graphics. +GPU Compute pipeline is programmed using Methane Kit and console UI is +implemented with amazing [FTXUI](https://github.com/ArthurSonzogni/FTXUI) library: + - [ConsoleApp.h](ConsoleApp.h) - console application base class implementing console UI. + - [ConsoleApp.cpp](ConsoleApp.cpp) + - [ConsoleComputeApp.h](ConsoleComputeApp.h) - compute application implements Gave of Life logic using Methane Kit. + - [ConsoleComputeApp.cpp](ConsoleComputeApp.cpp) + - [Shaders/GameOfLife.hlsl](Shaders/GameOfLife.hlsl) - HLSL compute shader implements Game of Life cells update iteration. + +Tutorial demonstrates the following techniques: + - Using [ComputeContext](/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeContext.h) for +GPU-accelerated compute operations from console application without GUI Window. + - Using [ComputeState](/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeState.h) with compute program +for dispatching compute operations on GPU. + - Using [ComputeCommandList](/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeCommandList.h) for recording +commands for dispatching compute operation and executing it in the compute command queue. + - Using 2D Texture both as Input and Output of compute program data (bound as UAV, Unordered Access View). + - Transferring texture data both to GPU and from GPU for read-back on CPU for presenting results in console UI. + - Using [FTXUI](https://github.com/ArthurSonzogni/FTXUI) library for creating GUI-like user interfaces in console. + +## Game of Life Rules + +The universe of the Game of Life is an infinite, two-dimensional orthogonal grid of square cells, +each of which is in one of two possible states, live or dead (or populated and unpopulated, respectively). +Every cell interacts with its eight neighbours, which are the cells that are horizontally, vertically, or diagonally adjacent. +At each step in time, the following transitions occur: + - Any live cell with fewer than two live neighbours dies, as if by underpopulation. + - Any live cell with two or three live neighbours lives on to the next generation. + - Any live cell with more than three live neighbours dies, as if by overpopulation. + - Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction. + +## Compute Initialization + +GPU compute initialization using Methane Kit includes creating `ComputeContext`, `ComputeState` and `ComputeCommandList`. +Compute state duplicates compute [thread group size](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-attributes-numthreads) +from [HLSL compute shader](#hlsl-compute-shader) due to API differences between DirectX, Vulkan +(define thread group size in shader) and Metal (defines thread group size in dispatch command arguments). + +```cpp +void ConsoleComputeApp::Init() +{ + META_FUNCTION_TASK(); + const rhi::Device* device_ptr = GetComputeDevice(); + m_compute_context = device_ptr->CreateComputeContext(m_parallel_executor, {}); + m_compute_context.SetName("Game of Life"); + + m_compute_state = m_compute_context.CreateComputeState({ + m_compute_context.CreateProgram({ + rhi::Program::ShaderSet { { rhi::ShaderType::Compute, { data::ShaderProvider::Get(), { "GameOfLife", "MainCS" } } } }, + rhi::ProgramInputBufferLayouts { }, + rhi::ProgramArgumentAccessors + { + { { rhi::ShaderType::Compute, "m_frame_texture" }, rhi::ProgramArgumentAccessor::Type::Mutable }, + }, + }), + rhi::ThreadGroupSize(16U, 16U, 1U) + }); + m_compute_state.GetProgram().SetName("Game of Life Program"); + m_compute_state.SetName("Game of Life Compute State"); + + m_compute_cmd_list = m_compute_context.GetComputeCommandKit().GetQueue().CreateComputeCommandList(); + m_compute_cmd_list.SetName("Game of Life Compute"); + m_compute_cmd_list_set = rhi::CommandListSet({ m_compute_cmd_list.GetInterface() }); + + ... +} +``` + +Additionally, 2D Image Texture is created with `R8Uint` pixel format which is used for `ShaderRead`, `ShaderWrite` and `Readback` +of texture data after compute iteration. `ProgramBindings` instance is created for binding the texture to compute shader +argument `g_frame_texture` defined in [HLSL shader](#hlsl-compute-shader). Texture is initialized with random cells data +on CPU and uploaded to the GPU using transfer queue. + +```cpp +void ConsoleComputeApp::Init() +{ + ... + + rhi::TextureSettings frame_texture_settings = rhi::TextureSettings::ForImage( + gfx::Dimensions(GetFieldSize()), + std::nullopt, gfx::PixelFormat::R8Uint, false, + rhi::ResourceUsageMask{ + rhi::ResourceUsage::ShaderRead, + rhi::ResourceUsage::ShaderWrite, + rhi::ResourceUsage::ReadBack + } + ); + m_frame_texture = m_compute_context.CreateTexture(frame_texture_settings); + m_frame_texture.SetName("Game of Life Frame Texture"); + + m_compute_bindings = m_compute_state.GetProgram().CreateBindings({ + { { rhi::ShaderType::Compute, "g_frame_texture" }, { { m_frame_texture.GetInterface() } } }, + }); + m_compute_bindings.SetName("Game of Life Compute Bindings"); + + RandomizeFrameData(); + + // Complete bindings and texture initialization + m_compute_context.CompleteInitialization(); +} +``` + +## Compute Iteration + +On each iteration compute commands are recorded in `ComputeCommandList`: dispatch command performs execution of +compute shader from compute state. Then recorded command list is executed in compute command queue. +After compute execution is completed, frame texture data is read back on CPU for presenting in console UI. + +```cpp +void ConsoleComputeApp::Compute() +{ + META_FUNCTION_TASK(); + const data::FrameSize& field_size = GetFieldSize(); + const rhi::CommandQueue& compute_cmd_queue = m_compute_context.GetComputeCommandKit().GetQueue(); + const rhi::ThreadGroupSize& thread_group_size = m_compute_state.GetSettings().thread_group_size; + const rhi::ThreadGroupsCount thread_groups_count(data::DivCeil(field_size.GetWidth(), thread_group_size.GetWidth()), + data::DivCeil(field_size.GetHeight(), thread_group_size.GetHeight()), + 1U); + + META_DEBUG_GROUP_VAR(s_debum_group, "Compute Frame"); + m_compute_cmd_list.ResetWithState(m_compute_state, &s_debum_group); + m_compute_cmd_list.SetProgramBindings(m_compute_bindings); + m_compute_cmd_list.Dispatch(thread_groups_count); + m_compute_cmd_list.Commit(); + + compute_cmd_queue.Execute(m_compute_cmd_list_set); + m_compute_context.WaitForGpu(rhi::ContextWaitFor::ComputeComplete); + m_frame_data = m_frame_texture.GetData(compute_cmd_queue); + m_fps_counter.OnCpuFrameReadyToPresent(); +} +``` + +## HLSL Compute Shader + +Compute shader implements compute iteration of cells data in texture `g_frame_texture` (bound for read and write), +updated according to [Game of Life rules](#game-of-life-rules). + +```hlsl +RWTexture2D g_frame_texture; + +[numthreads(16, 16, 1)] +void MainCS(uint3 id : SV_DispatchThreadID) +{ + uint2 frame_texture_size; + g_frame_texture.GetDimensions(frame_texture_size.x, frame_texture_size.y); + if (id.x > frame_texture_size.x || id.y > frame_texture_size.y) + return; + + // For a cell at id.xy compute number live neighbours, + // which are 8 cells that are horizontally, vertically, or diagonally adjacent. + uint alive_neighbors_count = 0; + for (int x = -1; x <= 1; x++) + { + for (int y = -1; y <= 1; y++) + { + if (x == 0 && y == 0) + continue; + + uint2 neighbor_pos = uint2(id.x + x, id.y + y); + if (g_frame_texture[neighbor_pos.xy] > 0) + alive_neighbors_count++; + } + } + + if (g_frame_texture[id.xy] > 0) + { + // Any live cell with two or three live neighbours survives. + // All other live cells die in the next generation. + g_frame_texture[id.xy] = (alive_neighbors_count == 2 || alive_neighbors_count == 3) ? 1 : 0; + } + else + { + // Any dead cell with three live neighbours becomes a live cell. + // All other dead cells stay dead. + g_frame_texture[id.xy] = (alive_neighbors_count == 3) ? 1 : 0; + } +} +``` diff --git a/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeLinVulkan.jpg b/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeLinVulkan.jpg new file mode 100644 index 000000000..5b636c8e4 Binary files /dev/null and b/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeLinVulkan.jpg differ diff --git a/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeMacMetal.jpg b/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeMacMetal.jpg new file mode 100644 index 000000000..40e2cb162 Binary files /dev/null and b/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeMacMetal.jpg differ diff --git a/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeWinDirectX12.jpg b/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeWinDirectX12.jpg new file mode 100644 index 000000000..91604e4e7 Binary files /dev/null and b/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeWinDirectX12.jpg differ diff --git a/Apps/08-ConsoleCompute/Shaders/GameOfLife.hlsl b/Apps/08-ConsoleCompute/Shaders/GameOfLife.hlsl new file mode 100644 index 000000000..ea8c70c8a --- /dev/null +++ b/Apps/08-ConsoleCompute/Shaders/GameOfLife.hlsl @@ -0,0 +1,62 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: MethaneKit/Apps/Tutorials/08-ConsoleCompute/Shaders/GameOfLife.hlsl +Compute shader for Conway's Game of Life + +******************************************************************************/ + +RWTexture2D g_frame_texture; + +[numthreads(16, 16, 1)] +void MainCS(uint3 id : SV_DispatchThreadID) +{ + uint2 frame_texture_size; + g_frame_texture.GetDimensions(frame_texture_size.x, frame_texture_size.y); + if (id.x > frame_texture_size.x || id.y > frame_texture_size.y) + return; + + // For a cell at id.xy compute number live neighbours, + // which are 8 cells that are horizontally, vertically, or diagonally adjacent. + uint alive_neighbors_count = 0; + for (int x = -1; x <= 1; x++) + { + for (int y = -1; y <= 1; y++) + { + if (x == 0 && y == 0) + continue; + + uint2 neighbor_pos = uint2(id.x + x, id.y + y); + if (g_frame_texture[neighbor_pos.xy] > 0) + alive_neighbors_count++; + } + } + + if (g_frame_texture[id.xy] > 0) + { + // Any live cell with two or three live neighbours survives. + // All other live cells die in the next generation. + g_frame_texture[id.xy] = (alive_neighbors_count == 2 || alive_neighbors_count == 3) ? 1 : 0; + } + else + { + // Any dead cell with three live neighbours becomes a live cell. + // All other dead cells stay dead. + g_frame_texture[id.xy] = (alive_neighbors_count == 3) ? 1 : 0; + } +} diff --git a/Apps/CMakeLists.txt b/Apps/CMakeLists.txt index 2798cd9c8..053e40f18 100644 --- a/Apps/CMakeLists.txt +++ b/Apps/CMakeLists.txt @@ -6,3 +6,4 @@ add_subdirectory(04-ShadowCube) add_subdirectory(05-Typography) add_subdirectory(06-CubeMapArray) add_subdirectory(07-ParallelRendering) +add_subdirectory(08-ConsoleCompute) diff --git a/Apps/Common/Sources/Methane/Tutorials/AppSettings.cpp b/Apps/Common/Sources/Methane/Tutorials/AppSettings.cpp index 776777a9d..32160a12e 100644 --- a/Apps/Common/Sources/Methane/Tutorials/AppSettings.cpp +++ b/Apps/Common/Sources/Methane/Tutorials/AppSettings.cpp @@ -70,7 +70,7 @@ Graphics::CombinedAppSettings GetGraphicsTutorialAppSettings(const std::string& using namespace Methane::Graphics; constexpr rhi::RenderPassAccessMask default_screen_pass_access({ rhi::RenderPassAccess::ShaderResources, rhi::RenderPassAccess::Samplers }); - constexpr rhi::ContextOptionMask default_context_options; + constexpr rhi::ContextOptionMask default_context_options{ rhi::ContextOption::DeferredProgramBindingsInitialization }; const DepthStencilValues default_clear_depth_stencil(1.F, Graphics::Stencil(0)); const Color4F default_clear_color(0.0F, 0.2F, 0.4F, 1.0F); diff --git a/Apps/README.md b/Apps/README.md index 1bf864808..23a26d6d1 100644 --- a/Apps/README.md +++ b/Apps/README.md @@ -16,6 +16,7 @@ graphics rendering on simple examples using Methane Kit API in a cross-platform | 5. [Typography](05-Typography) | ![Typography on Windows](05-Typography/Screenshots/TypographyWinDirectX12.jpg) | Typography demonstrates animated text rendering with dynamic font atlas updates using Methane UI. | | 6. [Cube-Map Array](06-CubeMapArray) | ![Cube-Map Array on Windows](06-CubeMapArray/Screenshots/CubeMapArrayWinDirectX12.jpg) | Cube-map array texturing along with sky-box rendering. | | 7. [Parallel Rendering](07-ParallelRendering) | ![Parallel Rendering on Windows](07-ParallelRendering/Screenshots/ParallelRenderingWinDirectX12.jpg) | Parallel rendering of the textured cube instances to the single render pass. | +| 8. [Console Compute](08-ConsoleCompute) | ![Console Compute on Windows](08-ConsoleCompute/Screenshots/ConsoleComputeWinDirectX12.jpg) | Conway's Game of Life implemented in Compute Shader and running in pure console application. | ## Common diff --git a/Build/README.md b/Build/README.md index c180655df..e7a9f2a69 100644 --- a/Build/README.md +++ b/Build/README.md @@ -31,8 +31,8 @@ sudo apt-get update && sudo apt-get install build-essential git cmake lcov xcb libx11-dev libx11-xcb-dev libxcb-sync-dev libxcb-randr0-dev ``` - **MacOS** - - MacOS 10.15 "Catalina" or later - - XCode 11 or later with command-line tools + - MacOS 13 "Ventura" or later + - XCode 14 or later with command-line tools - **iOS / tvOS** - All MacOS prerequisites from above - iOS or tvOS simulator for running app in virtual environment diff --git a/Build/Unix/Build.sh b/Build/Unix/Build.sh index d8aa8be91..42dbad22c 100755 --- a/Build/Unix/Build.sh +++ b/Build/Unix/Build.sh @@ -10,7 +10,8 @@ BUILD_VERSION_MAJOR=0 BUILD_VERSION_MINOR=7 -BUILD_VERSION=$BUILD_VERSION_MAJOR.$BUILD_VERSION_MINOR +BUILD_VERSION_PATCH=2 +BUILD_VERSION=$BUILD_VERSION_MAJOR.$BUILD_VERSION_MINOR.$BUILD_VERSION_PATCH SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd ) SOURCE_DIR=$SCRIPT_DIR/../.. @@ -19,7 +20,7 @@ OUTPUT_DIR=$SCRIPT_DIR/../Output APPS_BUILD_ENABLED="ON" TESTS_BUILD_ENABLED="ON" PRECOMPILED_HEADERS_ENABLED="ON" -APPLE_DEPLOYMENT_TARGET="15.0" +APPLE_DEPLOYMENT_TARGET="16.0" # Parse command line arguments while [ $# -ne 0 ] @@ -135,6 +136,7 @@ fi CMAKE_FLAGS="$CMAKE_FLAGS \ -DMETHANE_VERSION_MAJOR=$BUILD_VERSION_MAJOR \ -DMETHANE_VERSION_MINOR=$BUILD_VERSION_MINOR \ + -DMETHANE_VERSION_PATCH=$BUILD_VERSION_PATCH \ -DMETHANE_GFX_VULKAN_ENABLED:BOOL=$VULKAN_BUILD_FLAG \ -DMETHANE_APPS_BUILD_ENABLED:BOOL=$APPS_BUILD_ENABLED \ -DMETHANE_TESTS_BUILD_ENABLED:BOOL=$TESTS_BUILD_ENABLED \ diff --git a/Build/Windows/Build.bat b/Build/Windows/Build.bat index aa534ecac..a08c7cfbe 100644 --- a/Build/Windows/Build.bat +++ b/Build/Windows/Build.bat @@ -10,7 +10,8 @@ SETLOCAL ENABLEDELAYEDEXPANSION SET BUILD_VERSION_MAJOR=0 SET BUILD_VERSION_MINOR=7 -SET BUILD_VERSION=%BUILD_VERSION_MAJOR%.%BUILD_VERSION_MINOR% +SET BUILD_VERSION_PATCH=2 +SET BUILD_VERSION=%BUILD_VERSION_MAJOR%.%BUILD_VERSION_MINOR%.%BUILD_VERSION_PATCH% SET OUTPUT_DIR=%~dp0..\Output SET SOURCE_DIR=%~dp0..\.. @@ -77,6 +78,7 @@ SET CMAKE_FLAGS= ^ -A %ARCH_TYPE% ^ -DMETHANE_VERSION_MAJOR=%BUILD_VERSION_MAJOR% ^ -DMETHANE_VERSION_MINOR=%BUILD_VERSION_MINOR% ^ + -DMETHANE_VERSION_PATCH=%BUILD_VERSION_PATCH% ^ -DMETHANE_GFX_VULKAN_ENABLED:BOOL=%VULKAN_API_ENABLED% ^ -DMETHANE_SHADERS_CODEVIEW_ENABLED:BOOL=ON ^ -DMETHANE_RHI_PIMPL_INLINE_ENABLED:BOOL=ON ^ diff --git a/CMake/MethaneGlobalOptions.cmake b/CMake/MethaneGlobalOptions.cmake index a3c9d1ea3..32de149d1 100644 --- a/CMake/MethaneGlobalOptions.cmake +++ b/CMake/MethaneGlobalOptions.cmake @@ -48,27 +48,26 @@ elseif(APPLE) endif() endif() + # Set OS deployment target minimum version + if(DEPLOYMENT_TARGET) + set(CMAKE_OSX_DEPLOYMENT_TARGET "${DEPLOYMENT_TARGET}") + else() + set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0") + endif() + + message(STATUS "METHANE OSX Minimum Deployment Target............ ${CMAKE_OSX_DEPLOYMENT_TARGET}") + # Apple platform specific definitions if (CMAKE_SYSTEM_NAME STREQUAL "iOS") set(APPLE_IOS 1) - # MacOS-only deployment target is set for iOS apps to allow running them natively on Macs with Apple Silicon - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") elseif (CMAKE_SYSTEM_NAME STREQUAL "tvOS") set(APPLE_TVOS 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(APPLE_MACOS 1) - if(NOT CMAKE_OSX_DEPLOYMENT_TARGET AND DEPLOYMENT_TARGET) - set(CMAKE_OSX_DEPLOYMENT_TARGET "${DEPLOYMENT_TARGET}") - endif() else() message(FATAL_ERROR "Methane Kit does not support Apple system: ${CMAKE_SYSTEM_NAME}") endif() - if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) - # Set default MacOS deployment target minimum version - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") - endif() - # Common code-signing options if (METHANE_APPLE_CODE_SIGNING_ENABLED) set(APPLE_CODE_SIGNING_FLAG "YES") diff --git a/CMake/MethaneShaders.cmake b/CMake/MethaneShaders.cmake index d17298904..757342027 100644 --- a/CMake/MethaneShaders.cmake +++ b/CMake/MethaneShaders.cmake @@ -31,6 +31,8 @@ function(get_shader_profile SHADER_TYPE PROFILE_VER OUT_PROFILE) set(_PROFILE_TYPE ps) elseif(SHADER_TYPE STREQUAL "vert") set(_PROFILE_TYPE vs) + elseif(SHADER_TYPE STREQUAL "comp") + set(_PROFILE_TYPE cs) else() message(FATAL_ERROR "Unsupported shader type: " ${SHADER_TYPE}) endif() @@ -185,6 +187,11 @@ function(compile_metal_shaders_to_library FOR_TARGET SDK METAL_SHADERS METAL_LIB FOLDER "Build/${FOR_TARGET}/Shaders" ) + set_target_properties(${FOR_TARGET} + PROPERTIES + METAL_LIB_TARGET ${METAL_LIB_TARGET} + ) + add_dependencies(${METAL_LIB_TARGET} ${_SHADER_COMPILE_TARGETS}) add_dependencies(${FOR_TARGET} ${METAL_LIB_TARGET}) endfunction() diff --git a/CMakeLists.txt b/CMakeLists.txt index 70053d619..275a8e54b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.21.0) # Methane version, build & product info set(METHANE_VERSION_MAJOR 0 CACHE STRING "Methane major version") set(METHANE_VERSION_MINOR 7 CACHE STRING "Methane minor version") -set(METHANE_VERSION_PATCH 1 CACHE STRING "Methane patch version") +set(METHANE_VERSION_PATCH 2 CACHE STRING "Methane patch version") set(METHANE_VERSION_BUILD 0 CACHE STRING "Methane build version") # Define CMake languages list diff --git a/CMakePresets.json b/CMakePresets.json index 394cbd0e4..32f38b5bc 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -786,7 +786,7 @@ }, "DEPLOYMENT_TARGET": { "type": "STRING", - "value": "15.0" + "value": "16.0" }, "ENABLE_ARC": { "type": "BOOL", diff --git a/Externals/CMakeLists.txt b/Externals/CMakeLists.txt index bf33f3c1f..15b175b82 100644 --- a/Externals/CMakeLists.txt +++ b/Externals/CMakeLists.txt @@ -38,7 +38,14 @@ include(FreeType2) include(HLSLpp) include(MagicEnum) include(TaskFlow) -include(Tracy) + +if(METHANE_APPS_BUILD_ENABLED) + include(FTXUI) +endif() + +if(METHANE_TRACY_PROFILING_ENABLED) + include(Tracy) +endif() if(METHANE_TESTS_BUILD_ENABLED) include(Catch2) diff --git a/Externals/CPM.cmake b/Externals/CPM.cmake index bf701b89b..3f8ca3814 100644 --- a/Externals/CPM.cmake +++ b/Externals/CPM.cmake @@ -1,4 +1,4 @@ -set(CPM_DOWNLOAD_VERSION 0.37.0) +set(CPM_DOWNLOAD_VERSION 0.38.0) if(CPM_SOURCE_CACHE) set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") diff --git a/Externals/FTXUI.cmake b/Externals/FTXUI.cmake new file mode 100644 index 000000000..b03aa32ac --- /dev/null +++ b/Externals/FTXUI.cmake @@ -0,0 +1,16 @@ +CPMAddPackage( + NAME FTXUI + GITHUB_REPOSITORY MethanePowered/FTXUI + GIT_TAG 436c237213a75107bea99a41a9672fca6c86de02 + #VERSION 4.0.0 + OPTIONS + "FTXUI_BUILD_DOCS OFF" + "FTXUI_BUILD_EXAMPLES OFF" + "FTXUI_BUILD_TESTS OFF" + "FTXUI_ENABLE_INSTALL OFF" +) + +set_target_properties(screen dom component + PROPERTIES + FOLDER Externals +) diff --git a/Externals/HLSLpp.cmake b/Externals/HLSLpp.cmake index c980f51ae..ba5cdd182 100644 --- a/Externals/HLSLpp.cmake +++ b/Externals/HLSLpp.cmake @@ -1,8 +1,8 @@ CPMAddPackage( NAME HLSLpp GITHUB_REPOSITORY MethanePowered/HLSLpp - GIT_TAG 3.2.1 - VERSION 3.2.1 + GIT_TAG 3.3.1 + VERSION 3.3.1 ) add_library(HLSLpp INTERFACE) diff --git a/Externals/README.md b/Externals/README.md index 984bd6f54..170b7b03d 100644 --- a/Externals/README.md +++ b/Externals/README.md @@ -19,7 +19,8 @@ to support parallel cmake configurations processing. | [DirectX Tex](https://github.com/microsoft/DirectXTex) | 1.9.6 | Static | [MIT](https://github.com/microsoft/DirectXTex/blob/main/LICENSE) | Texture processing library. | | [FMT](https://github.com/fmtlib/fmt) | 8.1.1 | Header-only | [Victor Zverovich](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst) | A modern formatting library. | | [FreeType](https://gitlab.freedesktop.org/freetype/freetype/) | 2.12.1 | Static | [FreeType (GPL 3.0)](https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/docs/FTL.TXT) | FreeType is a freely available software library to render fonts. | -| [HLSL++](https://github.com/redorav/hlslpp) | 3.2 | Header-only | [MIT](https://github.com/MethanePowered/HLSLpp/blob/master/LICENSE) | Math library using hlsl syntax with SSE/NEON support. | +| [FTXUI](https://github.com/ArthurSonzogni/FTXUI/) | 4.0.0 | Static | [MIT](https://github.com/ArthurSonzogni/FTXUI/blob/main/LICENSE) | C++ Functional Terminal User Interface. | +| [HLSL++](https://github.com/redorav/hlslpp) | 3.3.1 | Header-only | [MIT](https://github.com/MethanePowered/HLSLpp/blob/master/LICENSE) | Math library using hlsl syntax with SSE/NEON support. | | [ITT API](https://github.com/intel/ittapi) | 3.23.0 | Static | [BSD 3.0](https://github.com/MethanePowered/IttApi/blob/master/LICENSES/BSD-3-Clause.txt) | Intel® Instrumentation and Tracing Technology (ITT) and Just-In-Time (JIT) API. | | [Magic Enum](https://github.com/Neargye/magic_enum) | 0.8.0 | Header-only | [MIT](https://github.com/Neargye/magic_enum/blob/master/LICENSE) | Static reflection for enums (to string, from string, iteration) for modern C++, work with any enum type without any macro or boilerplate code. | | [OpenImageIO](https://github.com/OpenImageIO/oiio) | 2.0.5 | Static (optional) | [GPL 3.0](https://github.com/OpenCppCoverage/OpenCppCoverage/blob/master/LICENSE.txt) | Reading, writing, and processing images in a wide variety of file formats, using a format-agnostic API, aimed at VFX applications. | @@ -27,14 +28,14 @@ to support parallel cmake configurations processing. | [SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross) | 1.3.216.0 | Static | [Apache 2.0](https://github.com/KhronosGroup/SPIRV-Cross/blob/master/LICENSE) | A library for performing reflection on SPIR-V and disassembling SPIR-V back to high level languages. | | [STB](https://github.com/nothings/stb) | 2021-09-10 | Header-only | [MIT, Public Domain](https://github.com/nothings/stb/blob/master/LICENSE) | Single-file public domain libraries for C/C++. | | [TaskFlow](https://github.com/taskflow/taskflow) | 3.4.0 | Header-only | [MIT](https://github.com/taskflow/taskflow/blob/master/LICENSE) | A General-purpose Parallel and Heterogeneous Task Programming System. | -| [Tracy](https://github.com/wolfpld/tracy) | 0.9 | Static | [BSD 3.0](https://github.com/wolfpld/tracy/blob/master/LICENSE) | A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications. | +| [Tracy](https://github.com/wolfpld/tracy) | 0.9.1 | Static | [BSD 3.0](https://github.com/wolfpld/tracy/blob/master/LICENSE) | A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications. | | [Vulkan Headers](https://github.com/MethanePowered/VulkanHeaders) | 1.3.219 | Header-only | [Apache 2.0](https://github.com/KhronosGroup/Vulkan-Headers/blob/main/LICENSE.txt) | Vulkan Header files and API registry. | ## Build Tools | Script or Tool | Version | Usage | License | Description | |-------------------------------------------------------------------------------|-----------|--------------------------|------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------| -| [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) | 0.37.0 | CMake | [MIT](https://github.com/cpm-cmake/CPM.cmake/blob/master/LICENSE) | CMake's missing package manager. | +| [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) | 0.38.0 | CMake | [MIT](https://github.com/cpm-cmake/CPM.cmake/blob/master/LICENSE) | CMake's missing package manager. | | [CMRC](https://github.com/vector-of-bool/cmrc) | n/a | CMake | [MIT](https://github.com/vector-of-bool/cmrc/blob/master/LICENSE.txt) | A Resource Compiler in a Single CMake script. | | [CMake Modules](https://github.com/rpavlik/cmake-modules) | n/a | CMake | [BSL 1.0](https://github.com/microsoft/CMakeModules/blob/main/LICENSE_1_0.txt) | R.Pavlik collection of CMake modules. | | [iOS-Toolchain.cmake](https://github.com/leetal/ios-cmake) | 4.3.0 | CMake | [BSD 3.0](https://github.com/leetal/ios-cmake/blob/master/LICENSE.md) | A CMake toolchain file for iOS, macOS, watchOS & tvOS C/C++/Obj-C++ development. | | diff --git a/Externals/Tracy.cmake b/Externals/Tracy.cmake index bee246013..e769d48f1 100644 --- a/Externals/Tracy.cmake +++ b/Externals/Tracy.cmake @@ -1,8 +1,7 @@ CPMAddPackage( NAME Tracy GITHUB_REPOSITORY MethanePowered/Tracy - GIT_TAG ba78a788ae1ffd66a044452e459c3e55cea0ffcd # last commit in bugfix/tracy_0-9_instrumentation_semicolon - #VERSION 0.9 + VERSION 0.9.1 OPTIONS "TRACY_STATIC ON" "TRACY_ENABLE ${METHANE_TRACY_PROFILING_ENABLED}" diff --git a/Modules/Common/Instrumentation/CMakeLists.txt b/Modules/Common/Instrumentation/CMakeLists.txt index d2447fba3..5fcc72f7b 100644 --- a/Modules/Common/Instrumentation/CMakeLists.txt +++ b/Modules/Common/Instrumentation/CMakeLists.txt @@ -40,7 +40,7 @@ target_link_libraries(${TARGET} MethanePrimitives $<$:MethanePlatformUtils> # Logging functions $<$:ittnotify> - Tracy::TracyClient + $<$:Tracy::TracyClient> nowide PRIVATE MethaneBuildOptions diff --git a/Modules/Common/Instrumentation/Include/Methane/Instrumentation.h b/Modules/Common/Instrumentation/Include/Methane/Instrumentation.h index 7bf8af605..d69663b9e 100644 --- a/Modules/Common/Instrumentation/Include/Methane/Instrumentation.h +++ b/Modules/Common/Instrumentation/Include/Methane/Instrumentation.h @@ -41,8 +41,10 @@ Defines common ITT domain required for instrumentation. #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif +#ifdef TRACY_ENABLE #include #include +#endif #ifdef __GCC_COMPILER__ #pragma GCC diagnostic pop @@ -63,6 +65,10 @@ constexpr const char* g_methane_itt_domain_name = "Methane Kit"; ITT_DOMAIN_EXTERN(); +#ifdef TRACY_ENABLE + +#define TRACY_SET_THREAD_NAME(name) tracy::SetThreadName(name) + #if defined(TRACY_ZONE_CALL_STACK_DEPTH) && TRACY_ZONE_CALL_STACK_DEPTH > 0 #define TRACY_ZONE_SCOPED() ZoneScopedS(TRACY_ZONE_CALL_STACK_DEPTH) @@ -75,13 +81,18 @@ ITT_DOMAIN_EXTERN(); #endif // defined(TRACY_ZONE_CALL_STACK_DEPTH) && TRACY_ZONE_CALL_STACK_DEPTH > 0 -#ifdef TRACY_ENABLE - -#define TRACY_SET_THREAD_NAME(name) tracy::SetThreadName(name) - #else // ifdef TRACY_ENABLE #define TRACY_SET_THREAD_NAME(name) +#define TRACY_ZONE_SCOPED() +#define TRACY_ZONE_SCOPED_NAME(name) + +#define TracyMessage(S, N) +#define TracyLockable(M, V) M V +#define LockableBase(M) M +#define FrameMark +#define TracyCFrameMarkStart(name) +#define TracyCFrameMarkEnd(name) #endif // ifdef TRACY_ENABLE diff --git a/Modules/Common/Instrumentation/Sources/Methane/ScopeTimer.cpp b/Modules/Common/Instrumentation/Sources/Methane/ScopeTimer.cpp index 37c78e723..e93ebae0a 100644 --- a/Modules/Common/Instrumentation/Sources/Methane/ScopeTimer.cpp +++ b/Modules/Common/Instrumentation/Sources/Methane/ScopeTimer.cpp @@ -93,7 +93,9 @@ ScopeTimer::Registration ScopeTimer::Aggregator::RegisterScope(const char* scope m_new_scope_id++; m_timing_by_scope_id.resize(m_new_scope_id); m_counters_by_scope_id.emplace_back(ITT_COUNTER_INIT(scope_name_and_id_it->first, g_methane_itt_domain_name)); +#ifdef TRACY_ENABLE TracyPlotConfig(scope_name_and_id_it->first, tracy::PlotFormatType::Number, false, false, 0); +#endif } return Registration{ scope_name_and_id_it->first, scope_name_and_id_it->second }; } @@ -102,7 +104,10 @@ void ScopeTimer::Aggregator::AddScopeTiming(const Registration& scope_registrati { META_FUNCTION_TASK(); ITT_COUNTER_VALUE(m_counters_by_scope_id[scope_registration.id], std::chrono::duration_cast(duration).count()); + +#ifdef TRACY_ENABLE TracyPlot(scope_registration.name, std::chrono::duration_cast(duration).count()); +#endif if (scope_registration.id >= m_timing_by_scope_id.size()) { diff --git a/Modules/Data/Primitives/CMakeLists.txt b/Modules/Data/Primitives/CMakeLists.txt index 64136f4a9..67cb348ee 100644 --- a/Modules/Data/Primitives/CMakeLists.txt +++ b/Modules/Data/Primitives/CMakeLists.txt @@ -7,10 +7,14 @@ get_module_dirs("Methane/Data") set(HEADERS ${INCLUDE_DIR}/AlignedAllocator.hpp ${INCLUDE_DIR}/RectBinPack.hpp + ${INCLUDE_DIR}/IFpsCounter.h + ${INCLUDE_DIR}/FpsCounter.h ) set(SOURCES ${SOURCES_DIR}/Primitives.cpp + ${SOURCES_DIR}/IFpsCounter.cpp + ${SOURCES_DIR}/FpsCounter.cpp ) add_library(${TARGET} STATIC diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/FpsCounter.h b/Modules/Data/Primitives/Include/Methane/Data/FpsCounter.h similarity index 92% rename from Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/FpsCounter.h rename to Modules/Data/Primitives/Include/Methane/Data/FpsCounter.h index ce1fae862..0955a8f17 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/FpsCounter.h +++ b/Modules/Data/Primitives/Include/Methane/Data/FpsCounter.h @@ -23,17 +23,17 @@ FPS counter interface. #pragma once -#include +#include #include #include -namespace Methane::Graphics::Base +namespace Methane::Data { class FpsCounter - : public Rhi::IFpsCounter + : public IFpsCounter { public: FpsCounter() = default; diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IFpsCounter.h b/Modules/Data/Primitives/Include/Methane/Data/IFpsCounter.h similarity index 97% rename from Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IFpsCounter.h rename to Modules/Data/Primitives/Include/Methane/Data/IFpsCounter.h index 423b1aa46..98f8d8b62 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IFpsCounter.h +++ b/Modules/Data/Primitives/Include/Methane/Data/IFpsCounter.h @@ -16,7 +16,7 @@ limitations under the License. ******************************************************************************* -FILE: Methane/Graphics/RHI/IFpsCounter.h +FILE: Methane/Data/IFpsCounter.h FPS counter interface. ******************************************************************************/ @@ -25,7 +25,7 @@ FPS counter interface. #include -namespace Methane::Graphics::Rhi +namespace Methane::Data { class FrameTiming diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/FpsCounter.cpp b/Modules/Data/Primitives/Sources/Methane/Data/FpsCounter.cpp similarity index 97% rename from Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/FpsCounter.cpp rename to Modules/Data/Primitives/Sources/Methane/Data/FpsCounter.cpp index bc9fb36cf..44b01be04 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/FpsCounter.cpp +++ b/Modules/Data/Primitives/Sources/Methane/Data/FpsCounter.cpp @@ -21,13 +21,13 @@ FPS counter calculates frame time duration with moving average window algorithm. ******************************************************************************/ -#include +#include #include #include -namespace Methane::Graphics::Base +namespace Methane::Data { FpsCounter::FpsCounter(uint32_t averaged_timings_count) noexcept diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IFpsCounter.cpp b/Modules/Data/Primitives/Sources/Methane/Data/IFpsCounter.cpp similarity index 96% rename from Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IFpsCounter.cpp rename to Modules/Data/Primitives/Sources/Methane/Data/IFpsCounter.cpp index 9039c5b30..175c96ff1 100644 --- a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IFpsCounter.cpp +++ b/Modules/Data/Primitives/Sources/Methane/Data/IFpsCounter.cpp @@ -21,11 +21,11 @@ FPS counter interface. ******************************************************************************/ -#include +#include #include -namespace Methane::Graphics::Rhi +namespace Methane::Data { FrameTiming::FrameTiming(double total_time_sec, double present_time_sec, double gpu_wait_time_sec) noexcept diff --git a/Modules/Data/Types/Include/Methane/Data/Chunk.hpp b/Modules/Data/Types/Include/Methane/Data/Chunk.hpp index 62e2e9e7e..745a91b18 100644 --- a/Modules/Data/Types/Include/Methane/Data/Chunk.hpp +++ b/Modules/Data/Types/Include/Methane/Data/Chunk.hpp @@ -43,12 +43,34 @@ class Chunk // NOSONAR - rule of zero is not applicable , m_data_size(static_cast(m_data_storage.size())) { } + explicit Chunk(const Chunk& other) + : m_data_storage(other.m_data_storage) + , m_data_ptr(m_data_storage.empty() ? other.m_data_ptr : m_data_storage.data()) + , m_data_size(m_data_storage.empty() ? other.m_data_size : static_cast(m_data_storage.size())) + { } + explicit Chunk(Chunk&& other) noexcept : m_data_storage(std::move(other.m_data_storage)) , m_data_ptr(m_data_storage.empty() ? other.m_data_ptr : m_data_storage.data()) , m_data_size(m_data_storage.empty() ? other.m_data_size : static_cast(m_data_storage.size())) { } + Chunk& operator=(const Chunk& other) noexcept + { + m_data_storage = other.m_data_storage; + m_data_ptr = m_data_storage.empty() ? other.m_data_ptr : m_data_storage.data(); + m_data_size = m_data_storage.empty() ? other.m_data_size : static_cast(m_data_storage.size()); + return *this; + } + + Chunk& operator=(Chunk&& other) noexcept + { + m_data_storage = std::move(other.m_data_storage); + m_data_ptr = m_data_storage.empty() ? other.m_data_ptr : m_data_storage.data(); + m_data_size = m_data_storage.empty() ? other.m_data_size : static_cast(m_data_storage.size()); + return *this; + } + [[nodiscard]] bool IsEmptyOrNull() const noexcept { return !m_data_ptr || !m_data_size; } [[nodiscard]] bool IsDataStored() const noexcept { return !m_data_storage.empty(); } @@ -76,19 +98,12 @@ class Chunk // NOSONAR - rule of zero is not applicable return GetDataPtr() + GetDataSize(); } -protected: - explicit Chunk(const Chunk& other) noexcept - : m_data_storage(other.m_data_storage) - , m_data_ptr(m_data_storage.empty() ? other.m_data_ptr : m_data_storage.data()) - , m_data_size(m_data_storage.empty() ? other.m_data_size : static_cast(m_data_storage.size())) - { } - private: // Data storage is used only when m_data_storage is not managed by m_data_storage provider and // returned with chunk (when m_data_storage is loaded from file, for example) - Bytes m_data_storage; - const ConstRawPtr m_data_ptr = nullptr; - const Size m_data_size = 0U; + Bytes m_data_storage; + ConstRawPtr m_data_ptr = nullptr; + Size m_data_size = 0U; }; } // namespace Methane::Data diff --git a/Modules/Data/Types/Include/Methane/Data/EnumMask.hpp b/Modules/Data/Types/Include/Methane/Data/EnumMask.hpp index be852c024..61168de45 100644 --- a/Modules/Data/Types/Include/Methane/Data/EnumMask.hpp +++ b/Modules/Data/Types/Include/Methane/Data/EnumMask.hpp @@ -103,7 +103,7 @@ class EnumMask constexpr EnumMask& SetBitOn(Bit bit) noexcept { return *this |= bit; } constexpr EnumMask& SetBitOff(Bit bit) noexcept { return *this &= ~EnumMask(bit); } constexpr EnumMask& SetBit(Bit bit, bool on) noexcept { return on ? SetBitOn(bit) : SetBitOff(bit); } - constexpr bool HasBits(EnumMask mask) const noexcept { return mask.m_value ? ((m_value & mask.GetValue()) == mask.GetValue()) : !m_value; } + constexpr bool HasBits(EnumMask mask) const noexcept { return mask.m_value ? ((m_value & mask.GetValue()) == mask.GetValue()) : true; } constexpr bool HasBit(Bit bit) const noexcept { return HasBits(EnumMask(bit)); } constexpr bool HasAnyBits(EnumMask mask) const noexcept { return (m_value & mask.GetValue()) != M{}; } constexpr bool HasAnyBit(Bit bit) const noexcept { return HasAnyBits(EnumMask(bit)); } diff --git a/Modules/Graphics/App/Sources/Methane/Graphics/AppBase.cpp b/Modules/Graphics/App/Sources/Methane/Graphics/AppBase.cpp index 24eb42e92..274763cb6 100644 --- a/Modules/Graphics/App/Sources/Methane/Graphics/AppBase.cpp +++ b/Modules/Graphics/App/Sources/Methane/Graphics/AppBase.cpp @@ -354,10 +354,10 @@ void AppBase::UpdateWindowTitle() return; } - const Rhi::RenderContextSettings& context_settings = m_context.GetSettings(); - const Rhi::IFpsCounter& fps_counter = m_context.GetFpsCounter(); - const uint32_t average_fps = fps_counter.GetFramesPerSecond(); - const Rhi::IFpsCounter::Timing average_frame_timing = fps_counter.GetAverageFrameTiming(); + const Rhi::RenderContextSettings& context_settings = m_context.GetSettings(); + const Data::IFpsCounter& fps_counter = m_context.GetFpsCounter(); + const uint32_t average_fps = fps_counter.GetFramesPerSecond(); + const Data::FrameTiming average_frame_timing = fps_counter.GetAverageFrameTiming(); const std::string title = fmt::format("{:s} {:d} FPS, {:.2f} ms, {:.2f}% CPU | {:d} x {:d} | {:d} FB | VSync {:s} | {:s} | {:s} | F1 - help", GetPlatformAppSettings().name, diff --git a/Modules/Graphics/Primitives/Include/Methane/Graphics/MeshBuffers.hpp b/Modules/Graphics/Primitives/Include/Methane/Graphics/MeshBuffers.hpp index ed24aedee..f3f1c40cd 100644 --- a/Modules/Graphics/Primitives/Include/Methane/Graphics/MeshBuffers.hpp +++ b/Modules/Graphics/Primitives/Include/Methane/Graphics/MeshBuffers.hpp @@ -46,7 +46,7 @@ class MeshBuffers // Uniform buffers are created separately in Frame dependent resources InstanceUniforms m_final_pass_instance_uniforms; - Rhi::SubResources m_final_pass_instance_uniforms_subresources; + Rhi::SubResource m_final_pass_instance_uniforms_subresource; public: template @@ -68,9 +68,9 @@ class MeshBuffers return static_cast(m_final_pass_instance_uniforms.size()); } - [[nodiscard]] const Rhi::SubResources& GetFinalPassUniformsSubresources() const + [[nodiscard]] const Rhi::SubResource& GetFinalPassUniformsSubresource() const { - return m_final_pass_instance_uniforms_subresources; + return m_final_pass_instance_uniforms_subresource; } [[nodiscard]] @@ -120,9 +120,10 @@ class MeshBuffers { META_FUNCTION_TASK(); m_final_pass_instance_uniforms.resize(instance_count); - m_final_pass_instance_uniforms_subresources = Rhi::SubResources{ - { reinterpret_cast(m_final_pass_instance_uniforms.data()), GetUniformsBufferSize() } // NOSONAR - }; + m_final_pass_instance_uniforms_subresource = Rhi::SubResource( + reinterpret_cast(m_final_pass_instance_uniforms.data()), // NOSONAR + GetUniformsBufferSize() + ); } }; diff --git a/Modules/Graphics/Primitives/Sources/Methane/Graphics/ImageLoader.cpp b/Modules/Graphics/Primitives/Sources/Methane/Graphics/ImageLoader.cpp index 57874745c..c8fddf517 100644 --- a/Modules/Graphics/Primitives/Sources/Methane/Graphics/ImageLoader.cpp +++ b/Modules/Graphics/Primitives/Sources/Methane/Graphics/ImageLoader.cpp @@ -171,7 +171,7 @@ Rhi::Texture ImageLoader::LoadImageToTexture2D(const Rhi::CommandQueue& target_c image_data.GetDimensions(), std::nullopt, image_format, options.HasAnyBit(ImageOption::Mipmapped))); texture.SetName(texture_name); - texture.SetData({ { image_data.GetPixels().GetDataPtr(), image_data.GetPixels().GetDataSize() } }, target_cmd_queue); + texture.SetData(target_cmd_queue, { { image_data.GetPixels().GetDataPtr(), image_data.GetPixels().GetDataSize() } }); return texture; } @@ -208,13 +208,13 @@ Rhi::Texture ImageLoader::LoadImagesToTextureCube(const Rhi::CommandQueue& targe const uint32_t face_channels_count = face_images_data.front().second.GetChannelsCount(); META_CHECK_ARG_EQUAL_DESCR(face_dimensions.GetWidth(), face_dimensions.GetHeight(), "all images of cube texture faces must have equal width and height"); - Rhi::IResource::SubResources face_resources; - face_resources.reserve(face_images_data.size()); + Rhi::IResource::SubResources face_sub_resources; + face_sub_resources.reserve(face_images_data.size()); for(const auto& [face_index, image_data] : face_images_data) { META_CHECK_ARG_EQUAL_DESCR(face_dimensions, image_data.GetDimensions(), "all face image of cube texture must have equal dimensions"); META_CHECK_ARG_EQUAL_DESCR(face_channels_count, image_data.GetChannelsCount(), "all face image of cube texture must have equal channels count"); - face_resources.emplace_back(image_data.GetPixels().GetDataPtr(), image_data.GetPixels().GetDataSize(), Rhi::IResource::SubResource::Index(face_index)); + face_sub_resources.emplace_back(image_data.GetPixels().GetDataPtr(), image_data.GetPixels().GetDataSize(), Rhi::IResource::SubResource::Index(face_index)); } // Load face images to cube texture @@ -224,7 +224,7 @@ Rhi::Texture ImageLoader::LoadImagesToTextureCube(const Rhi::CommandQueue& targe face_dimensions.GetWidth(), std::nullopt, image_format, options.HasAnyBit(Option::Mipmapped))); texture.SetName(texture_name); - texture.SetData(face_resources, target_cmd_queue); + texture.SetData(target_cmd_queue, face_sub_resources); return texture; } diff --git a/Modules/Graphics/Primitives/Sources/Methane/Graphics/MeshBuffersBase.cpp b/Modules/Graphics/Primitives/Sources/Methane/Graphics/MeshBuffersBase.cpp index 913a4fd39..a5a6cd145 100644 --- a/Modules/Graphics/Primitives/Sources/Methane/Graphics/MeshBuffersBase.cpp +++ b/Modules/Graphics/Primitives/Sources/Methane/Graphics/MeshBuffersBase.cpp @@ -53,12 +53,10 @@ MeshBuffersBase::MeshBuffersBase(const Rhi::CommandQueue& render_cmd_queue, cons mesh_data.GetVertexDataSize(), mesh_data.GetVertexSize())); vertex_buffer.SetName(fmt::format("{} Vertex Buffer", mesh_name)); - vertex_buffer.SetData({ - { - mesh_data.GetVertexData(), // NOSONAR - mesh_data.GetVertexDataSize() - } - }, render_cmd_queue); + vertex_buffer.SetData(render_cmd_queue, { + mesh_data.GetVertexData(), + mesh_data.GetVertexDataSize() + }); m_vertex_buffer_set = Rhi::BufferSet(Rhi::BufferType::Vertex, { vertex_buffer }); m_index_buffer = Rhi::Buffer(m_context, @@ -66,12 +64,10 @@ MeshBuffersBase::MeshBuffersBase(const Rhi::CommandQueue& render_cmd_queue, cons mesh_data.GetIndexDataSize(), GetIndexFormat(mesh_data.GetIndex(0)))); m_index_buffer.SetName(fmt::format("{} Index Buffer", mesh_name)); - m_index_buffer.SetData({ - { - reinterpret_cast(mesh_data.GetIndices().data()), // NOSONAR - mesh_data.GetIndexDataSize() - } - }, render_cmd_queue); + m_index_buffer.SetData(render_cmd_queue, { + reinterpret_cast(mesh_data.GetIndices().data()), // NOSONAR + mesh_data.GetIndexDataSize() + }); } Rhi::ResourceBarriers MeshBuffersBase::CreateBeginningResourceBarriers(const Rhi::Buffer* constants_buffer_ptr) const diff --git a/Modules/Graphics/Primitives/Sources/Methane/Graphics/ScreenQuad.cpp b/Modules/Graphics/Primitives/Sources/Methane/Graphics/ScreenQuad.cpp index 8880583d1..e286ff8ca 100644 --- a/Modules/Graphics/Primitives/Sources/Methane/Graphics/ScreenQuad.cpp +++ b/Modules/Graphics/Primitives/Sources/Methane/Graphics/ScreenQuad.cpp @@ -72,7 +72,7 @@ static std::string GetQuadName(const ScreenQuad::Settings& settings, const Rhi:: if (settings.alpha_blending_enabled) quad_name_ss << " with Alpha-Blending"; if (!macro_definitions.empty()) - quad_name_ss << " " << Rhi::IShader::ConvertMacroDefinitionsToString(macro_definitions); + quad_name_ss << " " << Rhi::ShaderMacroDefinition::ToString(macro_definitions); return quad_name_ss.str(); } @@ -211,13 +211,10 @@ class ScreenQuad::Impl s_quad_mesh.GetVertexDataSize(), s_quad_mesh.GetVertexSize())); vertex_buffer.SetName(s_vertex_buffer_name); - vertex_buffer.SetData({ - { - reinterpret_cast(s_quad_mesh.GetVertices().data()), // NOSONAR - s_quad_mesh.GetVertexDataSize() - } - }, - m_render_cmd_queue); + vertex_buffer.SetData(m_render_cmd_queue, { + reinterpret_cast(s_quad_mesh.GetVertices().data()), // NOSONAR + s_quad_mesh.GetVertexDataSize() + }); render_context.GetObjectRegistry().AddGraphicsObject(vertex_buffer.GetInterface()); m_vertex_buffer_set = Rhi::BufferSet(Rhi::BufferType::Vertex, { vertex_buffer }); } @@ -235,13 +232,10 @@ class ScreenQuad::Impl s_quad_mesh.GetIndexDataSize(), GetIndexFormat(s_quad_mesh.GetIndex(0)))); m_index_buffer.SetName(s_index_buffer_name); - m_index_buffer.SetData({ - { - reinterpret_cast(s_quad_mesh.GetIndices().data()), // NOSONAR - s_quad_mesh.GetIndexDataSize() - } - }, - m_render_cmd_queue); + m_index_buffer.SetData(m_render_cmd_queue, { + reinterpret_cast(s_quad_mesh.GetIndices().data()), // NOSONAR + s_quad_mesh.GetIndexDataSize() + }); render_context.GetObjectRegistry().AddGraphicsObject(m_index_buffer.GetInterface()); } @@ -342,14 +336,10 @@ class ScreenQuad::Impl m_settings.blend_color.AsVector() }; - m_const_buffer.SetData({ - { - reinterpret_cast(&constants), // NOSONAR - static_cast(sizeof(constants)) - } - }, - m_render_cmd_queue - ); + m_const_buffer.SetData(m_render_cmd_queue, { + reinterpret_cast(&constants), // NOSONAR + static_cast(sizeof(constants)) + }); } [[nodiscard]] static Rhi::IShader::MacroDefinitions GetPixelShaderMacroDefinitions(TextureMode texture_mode) diff --git a/Modules/Graphics/Primitives/Sources/Methane/Graphics/SkyBox.cpp b/Modules/Graphics/Primitives/Sources/Methane/Graphics/SkyBox.cpp index 75af705c0..ce5af48aa 100644 --- a/Modules/Graphics/Primitives/Sources/Methane/Graphics/SkyBox.cpp +++ b/Modules/Graphics/Primitives/Sources/Methane/Graphics/SkyBox.cpp @@ -180,7 +180,7 @@ class SkyBox::Impl META_CHECK_ARG_TRUE(buffer_bindings.uniforms_buffer.IsInitialized()); META_CHECK_ARG_GREATER_OR_EQUAL(buffer_bindings.uniforms_buffer.GetDataSize(), sizeof(Uniforms)); - buffer_bindings.uniforms_buffer.SetData(m_mesh_buffers.GetFinalPassUniformsSubresources(), m_render_cmd_queue); + buffer_bindings.uniforms_buffer.SetData(m_render_cmd_queue, m_mesh_buffers.GetFinalPassUniformsSubresource()); META_DEBUG_GROUP_VAR(s_debug_group, "Sky-box rendering"); render_cmd_list.ResetWithStateOnce(m_render_state, &s_debug_group); diff --git a/Modules/Graphics/RHI/Base/CMakeLists.txt b/Modules/Graphics/RHI/Base/CMakeLists.txt index 675a4db4f..11317f46e 100644 --- a/Modules/Graphics/RHI/Base/CMakeLists.txt +++ b/Modules/Graphics/RHI/Base/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADERS ${INCLUDE_DIR}/System.h ${INCLUDE_DIR}/Context.h ${INCLUDE_DIR}/RenderContext.h + ${INCLUDE_DIR}/ComputeContext.h ${INCLUDE_DIR}/Fence.h ${INCLUDE_DIR}/Shader.h ${INCLUDE_DIR}/Program.h @@ -19,6 +20,7 @@ set(HEADERS ${INCLUDE_DIR}/RenderPattern.h ${INCLUDE_DIR}/RenderState.h ${INCLUDE_DIR}/ViewState.h + ${INCLUDE_DIR}/ComputeState.h ${INCLUDE_DIR}/ResourceBarriers.h ${INCLUDE_DIR}/Resource.h ${INCLUDE_DIR}/Buffer.h @@ -33,9 +35,9 @@ set(HEADERS ${INCLUDE_DIR}/CommandListDebugGroup.h ${INCLUDE_DIR}/RenderCommandList.h ${INCLUDE_DIR}/ParallelRenderCommandList.h + ${INCLUDE_DIR}/ComputeCommandList.h ${INCLUDE_DIR}/DescriptorManager.h ${INCLUDE_DIR}/QueryPool.h - ${INCLUDE_DIR}/FpsCounter.h ) set(SOURCES ${GRAPHICS_API_SOURCES} @@ -44,6 +46,7 @@ set(SOURCES ${GRAPHICS_API_SOURCES} ${SOURCES_DIR}/System.cpp ${SOURCES_DIR}/Context.cpp ${SOURCES_DIR}/RenderContext.cpp + ${SOURCES_DIR}/ComputeContext.cpp ${SOURCES_DIR}/Fence.cpp ${SOURCES_DIR}/Shader.cpp ${SOURCES_DIR}/Program.cpp @@ -51,6 +54,7 @@ set(SOURCES ${GRAPHICS_API_SOURCES} ${SOURCES_DIR}/ProgramBindings.cpp ${SOURCES_DIR}/RenderState.cpp ${SOURCES_DIR}/ViewState.cpp + ${SOURCES_DIR}/ComputeState.cpp ${SOURCES_DIR}/ResourceBarriers.cpp ${SOURCES_DIR}/Resource.cpp ${SOURCES_DIR}/Buffer.cpp @@ -67,9 +71,9 @@ set(SOURCES ${GRAPHICS_API_SOURCES} ${SOURCES_DIR}/CommandListDebugGroup.cpp ${SOURCES_DIR}/RenderCommandList.cpp ${SOURCES_DIR}/ParallelRenderCommandList.cpp + ${SOURCES_DIR}/ComputeCommandList.cpp ${SOURCES_DIR}/DescriptorManager.cpp ${SOURCES_DIR}/QueryPool.cpp - ${SOURCES_DIR}/FpsCounter.cpp ) add_library(${TARGET} STATIC @@ -88,7 +92,6 @@ target_link_libraries(${TARGET} PRIVATE MethaneBuildOptions MethaneCommonPrecompiledHeaders - MethaneDataPrimitives MethaneInstrumentation MethaneMathPrecompiledHeaders TaskFlow diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Buffer.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Buffer.h index 84de2806d..6885e9025 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Buffer.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Buffer.h @@ -42,9 +42,10 @@ class Buffer // IResource interface Data::Size GetDataSize(Data::MemoryState size_type = Data::MemoryState::Reserved) const noexcept override; - // Buffer interface + // IBuffer interface const Settings& GetSettings() const noexcept final { return m_settings; } - uint32_t GetFormattedItemsCount() const noexcept final; + uint32_t GetFormattedItemsCount() const noexcept final; + void SetData(Rhi::ICommandQueue&, const SubResource& sub_resource) override; private: Settings m_settings; diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandKit.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandKit.h index 729da541b..4aa8a1887 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandKit.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandKit.h @@ -53,6 +53,8 @@ class CommandKit final [[nodiscard]] Rhi::ICommandList& GetListForEncoding(Rhi::CommandListId cmd_list_id, std::string_view debug_group_name) const override; [[nodiscard]] Rhi::ICommandListSet& GetListSet(const std::vector& cmd_list_ids, Opt frame_index_opt) const override; [[nodiscard]] Rhi::IFence& GetFence(Rhi::CommandListId fence_id) const override; + void ExecuteListSet(const std::vector& cmd_list_ids, Opt frame_index_opt) const override; + void ExecuteListSetAndWaitForCompletion(const std::vector& cmd_list_ids, Opt frame_index_opt) const override; private: using CommandListIndex = uint32_t; diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandList.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandList.h index 0c951b403..0def35fe9 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandList.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandList.h @@ -62,7 +62,7 @@ class CommandList // NOSONAR - custom destructor is used for logging, class has { // Raw pointer is used for program bindings instead of smart pointer for performance reasons // to get rid of shared_from_this() overhead required to acquire smart pointer from reference - const ProgramBindings* program_bindings_ptr; + const ProgramBindings* program_bindings_ptr = nullptr; Ptrs retained_resources; }; @@ -110,8 +110,8 @@ class CommandList // NOSONAR - custom destructor is used for logging, class has virtual void ResetCommandState(); virtual void ApplyProgramBindings(ProgramBindings& program_bindings, Rhi::ProgramBindingsApplyBehaviorMask apply_behavior); - CommandState& GetCommandState() { return m_command_state; } - const CommandState& GetCommandState() const { return m_command_state; } + CommandState& GetCommandState() { return m_command_state; } + const CommandState& GetCommandState() const { return m_command_state; } void SetCommandListState(State state); void SetCommandListStateNoLock(State state); diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandListSet.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandListSet.h index c866b2cb8..b08443f09 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandListSet.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/CommandListSet.h @@ -27,8 +27,7 @@ Base implementation of the command list set interface. #include #include - -#include +#include #include #include diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ComputeCommandList.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ComputeCommandList.h new file mode 100644 index 000000000..300551cce --- /dev/null +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ComputeCommandList.h @@ -0,0 +1,56 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Base/ComputeCommandList.h +Base implementation of the compute command list interface. + +******************************************************************************/ + +#pragma once + +#include "CommandList.h" + +#include + +namespace Methane::Graphics::Base +{ + +class ComputeState; + +class ComputeCommandList + : public Rhi::IComputeCommandList + , public CommandList +{ +public: + explicit ComputeCommandList(CommandQueue& command_queue); + + using CommandList::Reset; + + // IComputeCommandList interface + void ResetWithState(Rhi::IComputeState& compute_state, IDebugGroup* debug_group_ptr = nullptr) final; + void ResetWithStateOnce(Rhi::IComputeState& compute_state, IDebugGroup* debug_group_ptr = nullptr) final; + void SetComputeState(Rhi::IComputeState& compute_state) final; + void Dispatch(const Rhi::ThreadGroupsCount& thread_groups_count) override; + + ComputeState& GetComputeState(); + +private: + Ptr m_compute_state_ptr; +}; + +} // namespace Methane::Graphics::Base diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ComputeContext.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ComputeContext.h new file mode 100644 index 000000000..0a3c0d3ab --- /dev/null +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ComputeContext.h @@ -0,0 +1,59 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Base/ComputeContext.h +Base implementation of the compute context interface. + +******************************************************************************/ + +#pragma once + +#include "Context.h" + +#include + +namespace Methane::Graphics::Base +{ + +class ComputeContext + : public Context + , public Rhi::IComputeContext +{ +public: + ComputeContext(Device& device, UniquePtr&& descriptor_manager_ptr, + tf::Executor& parallel_executor, const Settings& settings); + + // Context interface + void Initialize(Device& device, bool is_callback_emitted = true) override; + void WaitForGpu(WaitFor wait_for) override; + [[nodiscard]] OptionMask GetOptions() const noexcept final { return m_settings.options; } + bool UploadResources() const override; + + // IComputeContext interface + [[nodiscard]] const Settings& GetSettings() const noexcept override { return m_settings; } + +protected: + Rhi::IFence& GetComputeFence() const; + +private: + void WaitForGpuComputeComplete() const; + + Settings m_settings; +}; + +} // namespace Methane::Graphics::Base diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ComputeState.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ComputeState.h new file mode 100644 index 000000000..7049831e9 --- /dev/null +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ComputeState.h @@ -0,0 +1,59 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Base/ComputeState.h +Base implementation of the compute state interface. + +******************************************************************************/ + +#pragma once + +#include "Object.h" + +#include + +namespace Methane::Graphics::Base +{ + +class ComputeCommandList; + +class ComputeState + : public Object + , public Rhi::IComputeState +{ +public: + ComputeState(const Rhi::IContext& context, const Settings& settings); + + // IComputeState overrides + const Settings& GetSettings() const noexcept override { return m_settings; } + void Reset(const Settings& settings) override; + + // ComputeState interface + virtual void Apply(ComputeCommandList& command_list) = 0; + + const Rhi::IContext& GetContext() const noexcept { return m_context; } + +protected: + Rhi::IProgram& GetProgram(); + +private: + const Rhi::IContext& m_context; + Settings m_settings; +}; + +} // namespace Methane::Graphics::Base diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Context.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Context.h index 4a6edaeb7..bad510b8e 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Context.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Context.h @@ -81,6 +81,7 @@ class Context Rhi::ICommandKit& GetDefaultCommandKit(Rhi::CommandListType type) const final; Rhi::ICommandKit& GetDefaultCommandKit(Rhi::ICommandQueue& cmd_queue) const final; const Rhi::IDevice& GetDevice() const final; + bool UploadResources() const override; // Context interface virtual void Initialize(Device& device, bool is_callback_emitted = true); @@ -89,10 +90,10 @@ class Context // IObject interface bool SetName(std::string_view name) override; - DeferredAction GetRequestedAction() const noexcept { return m_requested_action; } - Ptr GetBaseDevicePtr() const noexcept { return m_device_ptr; } - Device& GetBaseDevice(); - const Device& GetBaseDevice() const; + DeferredAction GetRequestedAction() const noexcept { return m_requested_action; } + Ptr GetBaseDevicePtr() const noexcept { return m_device_ptr; } + Device& GetBaseDevice(); + const Device& GetBaseDevice() const; Rhi::IDescriptorManager& GetDescriptorManager() const; protected: @@ -100,7 +101,6 @@ class Context void SetDevice(Device& device); // Context interface - virtual bool UploadResources(); virtual void OnGpuWaitStart(WaitFor); virtual void OnGpuWaitComplete(WaitFor wait_for); diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/DescriptorManager.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/DescriptorManager.h index 2709219cd..fc5bfa818 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/DescriptorManager.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/DescriptorManager.h @@ -24,9 +24,8 @@ Base descriptor manager implementation. #pragma once #include - #include -#include +#include #include diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/RenderCommandList.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/RenderCommandList.h index 8bf1bc70c..5a9b44eef 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/RenderCommandList.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/RenderCommandList.h @@ -61,7 +61,7 @@ class RenderCommandList PrimitiveType }; - using ChangeMask = Methane::Data::EnumMask; + using ChangeMask = Data::EnumMask; Ptrs render_pass_attachments_ptr; Ptr render_state_ptr; diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/RenderContext.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/RenderContext.h index 90295eafa..775399066 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/RenderContext.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/RenderContext.h @@ -25,9 +25,9 @@ Base implementation of the render context interface. #include "Context.h" #include "Fence.h" -#include "FpsCounter.h" #include +#include namespace Methane::Graphics::Base { @@ -38,22 +38,23 @@ class RenderContext { public: RenderContext(Device& device, UniquePtr&& descriptor_manager_ptr, - tf::Executor& parallel_executor, const Settings& settings); + tf::Executor& parallel_executor, const Settings& settings); // IContext interface [[nodiscard]] OptionMask GetOptions() const noexcept final { return m_settings.options_mask; } void WaitForGpu(WaitFor wait_for) override; // IRenderContext interface - void Resize(const FrameSize& frame_size) override; - void Present() override; - const Settings& GetSettings() const noexcept final { return m_settings; } - uint32_t GetFrameBufferIndex() const noexcept final { return m_frame_buffer_index; } - uint32_t GetFrameIndex() const noexcept final { return m_frame_index; } - const Rhi::IFpsCounter& GetFpsCounter() const noexcept final { return m_fps_counter; } - bool SetVSyncEnabled(bool vsync_enabled) override; - bool SetFrameBuffersCount(uint32_t frame_buffers_count) override; - bool SetFullScreen(bool is_full_screen) override; + void Resize(const FrameSize& frame_size) override; + void Present() override; + const Settings& GetSettings() const noexcept final { return m_settings; } + uint32_t GetFrameBufferIndex() const noexcept final { return m_frame_buffer_index; } + uint32_t GetFrameIndex() const noexcept final { return m_frame_index; } + const Data::IFpsCounter& GetFpsCounter() const noexcept final { return m_fps_counter; } + bool SetVSyncEnabled(bool vsync_enabled) override; + bool SetFrameBuffersCount(uint32_t frame_buffers_count) override; + bool SetFullScreen(bool is_full_screen) override; + bool UploadResources() const final; // Context interface void Initialize(Device& device, bool is_callback_emitted = true) override; @@ -71,7 +72,6 @@ class RenderContext Rhi::IFence& GetRenderFence() const; // Context overrides - bool UploadResources() override; void OnGpuWaitStart(WaitFor wait_for) override; void OnGpuWaitComplete(WaitFor wait_for) override; @@ -82,10 +82,10 @@ class RenderContext void WaitForGpuRenderComplete(); void WaitForGpuFramePresented(); - Settings m_settings; - uint32_t m_frame_buffer_index = 0U; - uint32_t m_frame_index = 0U; - FpsCounter m_fps_counter; + Settings m_settings; + uint32_t m_frame_buffer_index = 0U; + uint32_t m_frame_index = 0U; + Data::FpsCounter m_fps_counter; }; } // namespace Methane::Graphics::Base diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Resource.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Resource.h index 7ec66ea12..0a238f71b 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Resource.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Resource.h @@ -49,47 +49,34 @@ class Resource Resource(Resource&&) = delete; // IResource interface - [[nodiscard]] Type GetResourceType() const noexcept final { return m_type; } - [[nodiscard]] State GetState() const noexcept final { return m_state; } - [[nodiscard]] const Opt& GetOwnerQueueFamily() const noexcept final { return m_owner_queue_family_index_opt; } - [[nodiscard]] UsageMask GetUsage() const noexcept final { return m_usage_mask; } - [[nodiscard]] const Rhi::IContext& GetContext() const noexcept final; - [[nodiscard]] const SubResource::Count& GetSubresourceCount() const noexcept final { return m_sub_resource_count; } - [[nodiscard]] Data::Size GetSubResourceDataSize(const SubResource::Index& subresource_index = SubResource::Index()) const final; - [[nodiscard]] SubResource GetData(const SubResource::Index& sub_resource_index = SubResource::Index(), - const std::optional& data_range = {}) override; + [[nodiscard]] Type GetResourceType() const noexcept final { return m_type; } + [[nodiscard]] State GetState() const noexcept final { return m_state; } + [[nodiscard]] const Opt& GetOwnerQueueFamily() const noexcept final { return m_owner_queue_family_index_opt; } + [[nodiscard]] UsageMask GetUsage() const noexcept final { return m_usage_mask; } + [[nodiscard]] const Rhi::IContext& GetContext() const noexcept final; + bool SetState(State state, Ptr& out_barriers) final; bool SetState(State state) final; bool SetOwnerQueueFamily(uint32_t family_index) final; bool SetOwnerQueueFamily(uint32_t family_index, Ptr& out_barriers) final; - void SetData(const SubResources& sub_resources, Rhi::ICommandQueue&) override; [[nodiscard]] Ptr& GetSetupTransitionBarriers() noexcept { return m_setup_transition_barriers_ptr; } protected: - [[nodiscard]] const Context& GetBaseContext() const noexcept { return m_context; } - [[nodiscard]] Data::Size GetInitializedDataSize() const noexcept { return m_initialized_data_size; } - - void SetSubResourceCount(const SubResource::Count& sub_resource_count); - void ValidateSubResource(const SubResource& sub_resource) const; - void ValidateSubResource(const SubResource::Index& sub_resource_index, const std::optional& sub_resource_data_range) const; - void SetStateChangeUpdatesBarriers(bool is_state_change_updates_barriers) { m_is_state_change_updates_barriers = is_state_change_updates_barriers; } + [[nodiscard]] const Context& GetBaseContext() const noexcept { return m_context; } + [[nodiscard]] Data::Size GetInitializedDataSize() const noexcept { return m_initialized_data_size; } + void SetInitializedDataSize(Data::Size initialized_data_size) noexcept { m_initialized_data_size = initialized_data_size; } - [[nodiscard]] virtual Data::Size CalculateSubResourceDataSize(const SubResource::Index& sub_resource_index) const; + void SetStateChangeUpdatesBarriers(bool is_state_change_updates_barriers) + { m_is_state_change_updates_barriers = is_state_change_updates_barriers; } private: - using SubResourceSizes = std::vector; - void FillSubresourceSizes(); - const Context& m_context; const Type m_type; const UsageMask m_usage_mask; State m_state; const Opt m_auto_transition_source_state_opt; - Data::Size m_initialized_data_size = 0U; - bool m_sub_resource_count_constant = false; - SubResource::Count m_sub_resource_count; - SubResourceSizes m_sub_resource_sizes; + Data::Size m_initialized_data_size = 0U; Ptr m_setup_transition_barriers_ptr; Opt m_owner_queue_family_index_opt; bool m_is_state_change_updates_barriers = true; diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ResourceBarriers.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ResourceBarriers.h index f0f3029b2..cddae3b8e 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ResourceBarriers.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/ResourceBarriers.h @@ -24,8 +24,7 @@ Methane resource barriers base implementation. #pragma once #include - -#include +#include #include diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Sampler.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Sampler.h index 58f8f0470..2df32db67 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Sampler.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Sampler.h @@ -42,10 +42,9 @@ class Sampler Opt auto_transition_source_state_opt = {}); // ISampler interface - const Settings& GetSettings() const override { return m_settings; } + const Settings& GetSettings() const final { return m_settings; } // IResource interface - void SetData(const SubResources& sub_resources, Rhi::ICommandQueue&) override; Data::Size GetDataSize(Data::MemoryState) const noexcept override { return 0; } private: diff --git a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Texture.h b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Texture.h index a0eeb9b07..00c3ea712 100644 --- a/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Texture.h +++ b/Modules/Graphics/RHI/Base/Include/Methane/Graphics/Base/Texture.h @@ -39,19 +39,29 @@ class Texture State initial_state = State::Undefined, Opt auto_transition_source_state_opt = {}); // ITexture interface - const Settings& GetSettings() const override { return m_settings; } - Data::Size GetDataSize(Data::MemoryState size_type = Data::MemoryState::Reserved) const noexcept override; + [[nodiscard]] const Settings& GetSettings() const override { return m_settings; } + [[nodiscard]] Data::Size GetDataSize(Data::MemoryState size_type = Data::MemoryState::Reserved) const noexcept override; + [[nodiscard]] SubResource::Count GetSubresourceCount() const noexcept final { return m_sub_resource_count; } + [[nodiscard]] Data::Size GetSubResourceDataSize(const SubResource::Index& subresource_index) const final; + void SetData(Rhi::ICommandQueue&, const SubResources& sub_resources) override; static Data::Size GetRequiredMipLevelsCount(const Dimensions& dimensions); protected: // Resource overrides - Data::Size CalculateSubResourceDataSize(const SubResource::Index& sub_resource_index) const override; + Data::Size CalculateSubResourceDataSize(const SubResource::Index& sub_resource_index) const; static void ValidateDimensions(DimensionType dimension_type, const Dimensions& dimensions, bool mipmapped); + void ValidateSubResource(const Rhi::SubResource& sub_resource) const; + void ValidateSubResource(const SubResource::Index& sub_resource_index, const std::optional& sub_resource_data_range) const; + private: - const Settings m_settings; + using SubResourceSizes = std::vector; + + const Settings m_settings; + SubResource::Count m_sub_resource_count; + SubResourceSizes m_sub_resource_sizes; }; } // namespace Methane::Graphics::Base diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Buffer.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Buffer.cpp index 42bf38139..dcb0d1d6a 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Buffer.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Buffer.cpp @@ -37,8 +37,6 @@ Buffer::Buffer(const Context& context, const Settings& settings, { META_FUNCTION_TASK(); META_CHECK_ARG_NOT_ZERO_DESCR(settings.size, "can not create buffer of zero size"); - - SetSubResourceCount(SubResource::Count()); } Data::Size Buffer::GetDataSize(Data::MemoryState size_type) const noexcept @@ -53,4 +51,16 @@ uint32_t Buffer::GetFormattedItemsCount() const noexcept return m_settings.item_stride_size > 0U ? GetDataSize(Data::MemoryState::Initialized) / m_settings.item_stride_size : 0U; } +void Buffer::SetData(Rhi::ICommandQueue&, const SubResource& sub_resource) +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_NAME_DESCR("sub_resource", !sub_resource.IsEmptyOrNull(), "can not set empty subresource data to buffer"); + META_CHECK_ARG_EQUAL(sub_resource.GetIndex(), SubResource::Index()); + + const Data::Size reserved_data_size = GetDataSize(Data::MemoryState::Reserved); + META_UNUSED(reserved_data_size); + META_CHECK_ARG_LESS_OR_EQUAL_DESCR(sub_resource.GetDataSize(), reserved_data_size, "can not set more data than allocated buffer size"); + SetInitializedDataSize(sub_resource.GetDataSize()); +} + } // namespace Methane::Graphics::Base \ No newline at end of file diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandKit.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandKit.cpp index 68eef2839..b57655ee2 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandKit.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandKit.cpp @@ -24,33 +24,61 @@ Methane command kit implementation. #include #include #include +#include #include #include +#include #include #include #include #include #include +#include +#include + #include +#include namespace Methane::Graphics::Base { static constexpr uint32_t g_max_cmd_lists_count = 32U; +static std::string GetCommandListNameById(Rhi::CommandListId id) +{ + META_FUNCTION_TASK(); + switch(static_cast(id)) + { + case Rhi::CommandListPurpose::Default: return "Default"; + case Rhi::CommandListPurpose::PreUploadSync: return "PreUploadSync"; + case Rhi::CommandListPurpose::PostUploadSync: return "PostUploadSync"; + } + return std::to_string(id); +} + CommandKit::CommandKit(const Rhi::IContext& context, Rhi::CommandListType cmd_list_type) : m_context(context) , m_cmd_list_type(cmd_list_type) -{ } +{ + META_FUNCTION_TASK(); + if (context.GetType() == Rhi::ContextType::Compute) + { + META_CHECK_ARG_NOT_EQUAL_DESCR(cmd_list_type, Rhi::CommandListType::Render, + "compute context can not be used to create render command queues"); + } + META_CHECK_ARG_NOT_EQUAL_DESCR(cmd_list_type, Rhi::CommandListType::ParallelRender, + "command queue should be created with Render type to support ParallelRender command lists"); +} CommandKit::CommandKit(Rhi::ICommandQueue& cmd_queue) : Object(cmd_queue.GetName()) , m_context(cmd_queue.GetContext()) , m_cmd_list_type(cmd_queue.GetCommandListType()) , m_cmd_queue_ptr(static_cast(cmd_queue).GetPtr()) -{ } +{ +} bool CommandKit::SetName(std::string_view name) { @@ -117,12 +145,13 @@ Rhi::ICommandList& CommandKit::GetList(Rhi::CommandListId cmd_list_id = 0U) cons switch (m_cmd_list_type) { - case Rhi::CommandListType::Transfer: cmd_list_ptr = Rhi::ITransferCommandList::Create(GetQueue()); break; + case Rhi::CommandListType::Transfer: cmd_list_ptr = GetQueue().CreateTransferCommandList(); break; case Rhi::CommandListType::Render: cmd_list_ptr = RenderCommandList::CreateForSynchronization(GetQueue()); break; + case Rhi::CommandListType::Compute: cmd_list_ptr = GetQueue().CreateComputeCommandList(); break; default: META_UNEXPECTED_ARG(m_cmd_list_type); } - cmd_list_ptr->SetName(fmt::format("{} Utility Command List {}", GetName(), cmd_list_id)); + cmd_list_ptr->SetName(fmt::format("{} Helper List {}", GetName(), GetCommandListNameById(cmd_list_id))); return *cmd_list_ptr; } @@ -189,6 +218,30 @@ Rhi::IFence& CommandKit::GetFence(Rhi::CommandListId fence_id) const return *fence_ptr; } +void CommandKit::ExecuteListSet(const std::vector& cmd_list_ids, Opt frame_index_opt) const +{ + META_FUNCTION_TASK(); + GetQueue().Execute(GetListSet(cmd_list_ids, frame_index_opt)); +} + +void CommandKit::ExecuteListSetAndWaitForCompletion(const std::vector& cmd_list_ids, Opt frame_index_opt) const +{ + META_FUNCTION_TASK(); + std::mutex execution_wait_mutex; + size_t executing_cmd_list_count = cmd_list_ids.size(); + std::condition_variable_any executing_cmd_list_set_condition_var; + GetQueue().Execute(GetListSet(cmd_list_ids, frame_index_opt), + [&executing_cmd_list_count, &execution_wait_mutex, &executing_cmd_list_set_condition_var](const Rhi::ICommandList&) + { + std::lock_guard lock(execution_wait_mutex); + executing_cmd_list_count--; + executing_cmd_list_set_condition_var.notify_one(); + }); + + std::unique_lock lock(execution_wait_mutex); + executing_cmd_list_set_condition_var.wait(lock, [&executing_cmd_list_count]() { return executing_cmd_list_count == 0U; }); +} + CommandKit::CommandListIndex CommandKit::GetCommandListIndexById(Rhi::CommandListId cmd_list_id) const noexcept { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandList.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandList.cpp index d9f5d8923..a463715f5 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandList.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandList.cpp @@ -144,7 +144,6 @@ void CommandList::SetProgramBindings(Rhi::IProgramBindings& program_bindings, Rh auto& program_bindings_base = static_cast(program_bindings); ApplyProgramBindings(program_bindings_base, apply_behavior); - if (constexpr Rhi::ProgramBindingsApplyBehaviorMask constant_once_and_changes_only({ Rhi::ProgramBindingsApplyBehavior::ConstantOnce, Rhi::ProgramBindingsApplyBehavior::ChangesOnly diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandQueue.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandQueue.cpp index 0df8337d1..d70a72a46 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandQueue.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/CommandQueue.cpp @@ -21,6 +21,7 @@ Base implementation of the command queue interface. ******************************************************************************/ +#include "Methane/Graphics/RHI/ICommandList.h" #include #include #include @@ -35,7 +36,15 @@ CommandQueue::CommandQueue(const Context& context, Rhi::CommandListType command_ : m_context(context) , m_device_ptr(context.GetBaseDevicePtr()) , m_command_lists_type(command_lists_type) -{ } +{ + if (context.GetType() == Rhi::ContextType::Compute) + { + META_CHECK_ARG_NOT_EQUAL_DESCR(command_lists_type, Rhi::CommandListType::Render, + "compute context can not be used to create render command queues"); + } + META_CHECK_ARG_NOT_EQUAL_DESCR(command_lists_type, Rhi::CommandListType::ParallelRender, + "command queue should be created with Render type to support ParallelRender command lists"); +} bool CommandQueue::SetName(std::string_view name) { diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ComputeCommandList.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ComputeCommandList.cpp new file mode 100644 index 000000000..9adb1c7b7 --- /dev/null +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ComputeCommandList.cpp @@ -0,0 +1,91 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Base/ComputeCommandList.cpp +Base implementation of the compute command list interface. + +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +namespace Methane::Graphics::Base +{ + +ComputeCommandList::ComputeCommandList(CommandQueue& command_queue) + : CommandList(command_queue, Type::Compute) +{ } + +void ComputeCommandList::ResetWithState(Rhi::IComputeState& compute_state, IDebugGroup* debug_group_ptr) +{ + META_FUNCTION_TASK(); + Reset(debug_group_ptr); + SetComputeState(compute_state); +} + +void ComputeCommandList::ResetWithStateOnce(Rhi::IComputeState& compute_state, IDebugGroup* debug_group_ptr) +{ + META_FUNCTION_TASK(); + if (GetState() == State::Encoding && m_compute_state_ptr.get() == std::addressof(compute_state)) + { + META_LOG("{} Command list '{}' was already RESET with the same compute state '{}'", magic_enum::enum_name(GetType()), GetName(), compute_state.GetName()); + return; + } + ResetWithState(compute_state, debug_group_ptr); +} + +void ComputeCommandList::SetComputeState(Rhi::IComputeState& compute_state) +{ + META_FUNCTION_TASK(); + META_LOG("{} Command list '{}' SET COMPUTE STATE '{}':\n{}", magic_enum::enum_name(GetType()), GetName(), compute_state.GetName(), static_cast(compute_state.GetSettings())); + + VerifyEncodingState(); + + const bool render_state_changed = m_compute_state_ptr.get() != std::addressof(compute_state); + auto& compute_state_base = static_cast(compute_state); + compute_state_base.Apply(*this); + + Ptr compute_state_object_ptr = compute_state_base.GetBasePtr(); + m_compute_state_ptr = std::static_pointer_cast(compute_state_object_ptr); + + if (render_state_changed) + { + RetainResource(compute_state_object_ptr); + } +} + +ComputeState& ComputeCommandList::GetComputeState() +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_NOT_NULL_DESCR(m_compute_state_ptr, "Compute command list '{}' state was not set.", GetName()); + return *m_compute_state_ptr; +} + +void ComputeCommandList::Dispatch([[maybe_unused]] const Rhi::ThreadGroupsCount& thread_groups_count) +{ + META_FUNCTION_TASK(); + META_LOG("{} Command list '{}' DISPATCH {} thread groups count.", + magic_enum::enum_name(GetType()), GetName(), thread_groups_count); +} + +} // namespace Methane::Graphics::Base diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ComputeContext.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ComputeContext.cpp new file mode 100644 index 000000000..746cb9683 --- /dev/null +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ComputeContext.cpp @@ -0,0 +1,94 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Base/ComputeContext.cpp +Base implementation of the compute context interface. + +******************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +namespace Methane::Graphics::Base +{ + +ComputeContext::ComputeContext(Device& device, UniquePtr&& descriptor_manager_ptr, + tf::Executor& parallel_executor, const Settings& settings) + : Context(device, std::move(descriptor_manager_ptr), parallel_executor, Type::Compute) + , m_settings(settings) +{ + META_FUNCTION_TASK(); +} + +void ComputeContext::WaitForGpu(WaitFor wait_for) +{ + META_FUNCTION_TASK(); + Context::WaitForGpu(wait_for); + + switch (wait_for) + { + case WaitFor::RenderComplete: + case WaitFor::ComputeComplete: + WaitForGpuComputeComplete(); break; + case WaitFor::ResourcesUploaded: break; // Handled in Context::WaitForGpu + default: META_UNEXPECTED_ARG(wait_for); + } +} + +void ComputeContext::WaitForGpuComputeComplete() const +{ + META_FUNCTION_TASK(); + META_SCOPE_TIMER("ComputeContextDX::WaitForGpu::ComputeComplete"); + GetComputeFence().FlushOnCpu(); + META_CPU_FRAME_DELIMITER(0, 0); +} + +Rhi::IFence& ComputeContext::GetComputeFence() const +{ + META_FUNCTION_TASK(); + return GetComputeCommandKit().GetFence(0U); +} + +void ComputeContext::Initialize(Device& device, bool is_callback_emitted) +{ + META_FUNCTION_TASK(); + Context::Initialize(device, false); + + if (is_callback_emitted) + { + Data::Emitter::Emit(&Rhi::IContextCallback::OnContextInitialized, *this); + } +} + +bool ComputeContext::UploadResources() const +{ + META_FUNCTION_TASK(); + if (!Context::UploadResources()) + return false; + + // Compute commands will wait for resources uploading completion in upload queue + GetUploadCommandKit().GetFence().FlushOnGpu(GetComputeCommandKit().GetQueue()); + return true; +} + +} // namespace Methane::Graphics::Base diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ComputeState.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ComputeState.cpp new file mode 100644 index 000000000..100b21241 --- /dev/null +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ComputeState.cpp @@ -0,0 +1,54 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Base/ComputeState.cpp +Base implementation of the compute state interface. + +******************************************************************************/ + +#include +#include + +#include +#include + +namespace Methane::Graphics::Base +{ + +ComputeState::ComputeState(const Rhi::IContext& context, const Settings& settings) + : m_context(context) + , m_settings(settings) +{ } + +void ComputeState::Reset(const Settings& settings) +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_NOT_NULL_DESCR(settings.program_ptr, "program is not initialized in render state settings"); + META_CHECK_ARG_NOT_NULL_DESCR(settings.program_ptr->GetShader(Rhi::ShaderType::Compute), "Program used in compute state must include compute shader"); + + m_settings = settings; +} + +Rhi::IProgram& ComputeState::GetProgram() +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_NOT_NULL(m_settings.program_ptr); + return *m_settings.program_ptr; +} + +} // namespace Methane::Graphics::Base diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Context.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Context.cpp index 9bf451d01..76d911db8 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Context.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Context.cpp @@ -39,12 +39,14 @@ namespace Methane::Graphics::Base static const std::array()> g_default_command_kit_names = { { "Upload", "Render", - "Parallel Render" + "Parallel Render", + "Compute" } }; #ifdef METHANE_LOGGING_ENABLED -static const std::array()> g_wait_for_names = {{ +static const std::array()> g_wait_for_names = {{ "Render Complete", + "Compute Complete", "Frame Present", "Resources Upload" }}; @@ -282,7 +284,7 @@ void Context::ExecuteSyncCommandLists(const Rhi::ICommandKit& upload_cmd_kit) co } } -bool Context::UploadResources() +bool Context::UploadResources() const { META_FUNCTION_TASK(); const Rhi::ICommandKit& upload_cmd_kit = GetUploadCommandKit(); diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Program.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Program.cpp index 332f549dc..794f64d5f 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Program.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Program.cpp @@ -94,32 +94,35 @@ void Program::InitArgumentBindings(const ArgumentAccessors& argument_accessors) shader_types_by_argument_name_map[shader_argument.GetName()].insert(shader_argument.GetShaderType()); } } - - // Replace bindings for argument set for all shader types in program to one binding set for argument with ShaderType::All - for (const auto& [argument_name, shader_types] : shader_types_by_argument_name_map) - { - if (shader_types != all_shader_types) - continue; - Ptr argument_binding_ptr; - for(Rhi::ShaderType shader_type : all_shader_types) + if (all_shader_types.size() > 1) + { + // Replace bindings for argument set for all shader types in program to one binding set for argument with ShaderType::All + for (const auto& [argument_name, shader_types]: shader_types_by_argument_name_map) { - const Argument argument{ shader_type, argument_name }; - auto binding_by_argument_it = m_binding_by_argument.find(argument); - META_CHECK_ARG_DESCR(argument, binding_by_argument_it != m_binding_by_argument.end(), "Resource binding was not initialized for for argument"); - if (argument_binding_ptr) - { - argument_binding_ptr->MergeSettings(*binding_by_argument_it->second); - } - else + if (shader_types != all_shader_types) + continue; + + Ptr argument_binding_ptr; + for (Rhi::ShaderType shader_type: all_shader_types) { - argument_binding_ptr = binding_by_argument_it->second; + const Argument argument{ shader_type, argument_name }; + auto binding_by_argument_it = m_binding_by_argument.find(argument); + META_CHECK_ARG_DESCR(argument, binding_by_argument_it != m_binding_by_argument.end(), "Resource binding was not initialized for for argument"); + if (argument_binding_ptr) + { + argument_binding_ptr->MergeSettings(*binding_by_argument_it->second); + } + else + { + argument_binding_ptr = binding_by_argument_it->second; + } + m_binding_by_argument.erase(binding_by_argument_it); } - m_binding_by_argument.erase(binding_by_argument_it); - } - META_CHECK_ARG_NOT_NULL_DESCR(argument_binding_ptr, "failed to create resource binding for argument '{}'", argument_name); - m_binding_by_argument.try_emplace(Argument{ Rhi::ShaderType::All, argument_name }, argument_binding_ptr); + META_CHECK_ARG_NOT_NULL_DESCR(argument_binding_ptr, "failed to create resource binding for argument '{}'", argument_name); + m_binding_by_argument.try_emplace(Argument{ Rhi::ShaderType::All, argument_name }, argument_binding_ptr); + } } if (m_context.GetType() != Rhi::IContext::Type::Render) diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ProgramBindings.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ProgramBindings.cpp index 71afed2b9..943581ebe 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ProgramBindings.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/ProgramBindings.cpp @@ -46,18 +46,27 @@ static Rhi::ResourceState GetBoundResourceTargetState(const Rhi::IResource& reso switch (resource_type) { case Rhi::IResource::Type::Buffer: + { // FIXME: state transition of DX upload heap resources should be reworked properly and made friendly with Vulkan // DX resource in upload heap can not be transitioned to any other state but initial GenericRead state - if (dynamic_cast(resource).GetSettings().storage_mode != Rhi::IBuffer::StorageMode::Private) + const Rhi::BufferSettings& buffer_settings = dynamic_cast(resource).GetSettings(); + if (buffer_settings.usage_mask.HasBit(Rhi::ResourceUsage::ShaderWrite)) + return Rhi::ResourceState::UnorderedAccess; + if (buffer_settings.storage_mode != Rhi::IBuffer::StorageMode::Private) return resource.GetState(); else if (is_constant_binding) return Rhi::ResourceState::ConstantBuffer; - break; + } break; case Rhi::IResource::Type::Texture: - if (dynamic_cast(resource).GetSettings().type == Rhi::ITexture::Type::DepthStencil) + { + const Rhi::TextureSettings& texture_settings = dynamic_cast(resource).GetSettings(); + if (texture_settings.usage_mask.HasBit(Rhi::ResourceUsage::ShaderWrite)) + return Rhi::ResourceState::UnorderedAccess; + if (texture_settings.usage_mask.HasBit(Rhi::ResourceUsage::ShaderRead) && + texture_settings.type == Rhi::ITexture::Type::DepthStencil) return Rhi::ResourceState::DepthRead; - break; + } break; default: break; @@ -220,19 +229,27 @@ Rhi::IProgramBindings::IArgumentBinding& ProgramBindings::Get(const Rhi::IProgra ProgramBindings::operator std::string() const { META_FUNCTION_TASK(); - bool is_first = true; - std::stringstream ss; + std::vector argument_binding_strings; + argument_binding_strings.reserve(m_binding_by_argument.size()); for (const auto& [program_argument, argument_binding_ptr] : m_binding_by_argument) { META_CHECK_ARG_NOT_NULL(argument_binding_ptr); - if (!is_first) + argument_binding_strings.push_back(static_cast(*argument_binding_ptr)); + } + + // Arguments are stored in unordered_set, so to get reliable output we need to sort them + std::sort(argument_binding_strings.begin(), argument_binding_strings.end()); + + std::stringstream ss; + for(const std::string& argument_binding_str : argument_binding_strings) + { + if (ss.rdbuf()->in_avail() > 0) ss << ";\n"; - ss << " - " << static_cast(*argument_binding_ptr); - is_first = false; + ss << " - " << argument_binding_str; } - ss << "."; + ss << "."; return ss.str(); } diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/RenderContext.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/RenderContext.cpp index bfc3e4bd6..ecd797b99 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/RenderContext.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/RenderContext.cpp @@ -148,7 +148,7 @@ void RenderContext::Initialize(Device& device, bool is_callback_emitted) } } -bool RenderContext::UploadResources() +bool RenderContext::UploadResources() const { META_FUNCTION_TASK(); if (!Context::UploadResources()) diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/RenderState.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/RenderState.cpp index 38f8cb1d6..8dacb8e97 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/RenderState.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/RenderState.cpp @@ -22,6 +22,7 @@ Base implementation of the render state interface. ******************************************************************************/ #include +#include #include #include @@ -39,6 +40,7 @@ void RenderState::Reset(const Settings& settings) META_FUNCTION_TASK(); META_CHECK_ARG_NOT_NULL_DESCR(settings.program_ptr, "program is not initialized in render state settings"); META_CHECK_ARG_NOT_NULL_DESCR(settings.render_pattern_ptr, "render pass pattern is not initialized in render state settings"); + META_CHECK_ARG_NOT_NULL_DESCR(settings.program_ptr->GetShader(Rhi::ShaderType::Vertex), "Program used in render state must include vertex shader"); m_settings = settings; } diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Resource.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Resource.cpp index ffd64833d..8007512b3 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Resource.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Resource.cpp @@ -35,30 +35,6 @@ Base implementation of the resource interface. #include #include -template<> -struct fmt::formatter -{ - [[nodiscard]] constexpr auto parse(const format_parse_context& ctx) const { return ctx.end(); } - - template - auto format(const Methane::Graphics::Rhi::SubResource::Index& index, FormatContext& ctx) - { - return format_to(ctx.out(), "{}", static_cast(index)); - } -}; - -template<> -struct fmt::formatter -{ - [[nodiscard]] constexpr auto parse(const format_parse_context& ctx) const { return ctx.end(); } - - template - auto format(const Methane::Graphics::Rhi::SubResource::Count& count, FormatContext& ctx) - { - return format_to(ctx.out(), "{}", static_cast(count)); - } -}; - namespace Methane::Graphics::Base { @@ -71,57 +47,12 @@ Resource::Resource(const Context& context, Type type, UsageMask usage_mask, , m_auto_transition_source_state_opt(auto_transition_source_state_opt) { } -void Resource::SetData(const SubResources& sub_resources, Rhi::ICommandQueue&) -{ - META_FUNCTION_TASK(); - META_CHECK_ARG_NOT_EMPTY_DESCR(sub_resources, "can not set buffer data from empty sub-resources"); - - Data::Size sub_resources_data_size = 0U; - for(const Rhi::SubResource& sub_resource : sub_resources) - { - META_CHECK_ARG_NAME_DESCR("sub_resource", !sub_resource.IsEmptyOrNull(), "can not set empty subresource data to buffer"); - sub_resources_data_size += sub_resource.GetDataSize(); - - if (m_sub_resource_count_constant) - { - META_CHECK_ARG_LESS(sub_resource.GetIndex(), m_sub_resource_count); - } - else - { - m_sub_resource_count += sub_resource.GetIndex(); - } - } - - const Data::Size reserved_data_size = GetDataSize(Data::MemoryState::Reserved); - META_UNUSED(reserved_data_size); - - META_CHECK_ARG_LESS_OR_EQUAL_DESCR(sub_resources_data_size, reserved_data_size, "can not set more data than allocated buffer size"); - m_initialized_data_size = sub_resources_data_size; - - if (!m_sub_resource_count_constant) - { - FillSubresourceSizes(); - } -} - -Rhi::IResource::SubResource Resource::GetData(const SubResource::Index&, const std::optional&) -{ - META_FUNCTION_NOT_IMPLEMENTED_RETURN_DESCR(IResource::SubResource(), "reading data is not allowed for this type of resource"); -} - const Rhi::IContext& Resource::GetContext() const noexcept { META_FUNCTION_TASK(); return m_context; } -Data::Size Resource::GetSubResourceDataSize(const SubResource::Index& sub_resource_index) const -{ - META_FUNCTION_TASK(); - META_CHECK_ARG_LESS(sub_resource_index, m_sub_resource_count); - return m_sub_resource_sizes[sub_resource_index.GetRawIndex(m_sub_resource_count)]; -} - bool Resource::SetState(State state, Ptr& out_barriers) { META_FUNCTION_TASK(); @@ -211,77 +142,4 @@ bool Resource::SetOwnerQueueFamily(uint32_t family_index) return true; } -void Resource::SetSubResourceCount(const SubResource::Count& sub_resource_count) -{ - META_FUNCTION_TASK(); - - m_sub_resource_count_constant = true; - m_sub_resource_count = sub_resource_count; - m_sub_resource_sizes.clear(); - - FillSubresourceSizes(); -} - -void Resource::ValidateSubResource(const Rhi::SubResource& sub_resource) const -{ - META_FUNCTION_TASK(); - ValidateSubResource(sub_resource.GetIndex(), sub_resource.GetDataRangeOptional()); - - const Data::Index sub_resource_raw_index = sub_resource.GetIndex().GetRawIndex(m_sub_resource_count); - const Data::Size sub_resource_data_size = m_sub_resource_sizes[sub_resource_raw_index]; - META_UNUSED(sub_resource_data_size); - - if (sub_resource.HasDataRange()) - { - META_CHECK_ARG_EQUAL_DESCR(sub_resource.GetDataSize(), sub_resource.GetDataRange().GetLength(), - "sub-resource {} data size should be equal to the length of data range", sub_resource.GetIndex()); - } - META_CHECK_ARG_LESS_OR_EQUAL_DESCR(sub_resource.GetDataSize(), sub_resource_data_size, - "sub-resource {} data size should be less or equal than full resource size", sub_resource.GetIndex()); -} - -void Resource::ValidateSubResource(const SubResource::Index& sub_resource_index, const std::optional& sub_resource_data_range) const -{ - META_FUNCTION_TASK(); - META_CHECK_ARG_LESS(sub_resource_index, m_sub_resource_count); - if (!sub_resource_data_range) - return; - - META_CHECK_ARG_NAME_DESCR("sub_resource_data_range", !sub_resource_data_range->IsEmpty(), "sub-resource {} data range can not be empty", sub_resource_index); - const Data::Index sub_resource_raw_index = sub_resource_index.GetRawIndex(m_sub_resource_count); - META_CHECK_ARG_LESS(sub_resource_raw_index, m_sub_resource_sizes.size()); - - const Data::Size sub_resource_data_size = m_sub_resource_sizes[sub_resource_raw_index]; - META_UNUSED(sub_resource_data_size); - META_CHECK_ARG_LESS_DESCR(sub_resource_data_range->GetEnd(), sub_resource_data_size + 1, "sub-resource index {}", sub_resource_index); -} - -Data::Size Resource::CalculateSubResourceDataSize(const SubResource::Index& subresource_index) const -{ - META_FUNCTION_TASK(); - static const SubResource::Index s_zero_index; - META_CHECK_ARG_EQUAL_DESCR(subresource_index, s_zero_index, "subresource size is undefined, must be provided by super class override"); - - static const SubResource::Count s_one_count; - META_CHECK_ARG_EQUAL_DESCR(m_sub_resource_count, s_one_count, "subresource size is undefined, must be provided by super class override"); - - return GetDataSize(); -} - -void Resource::FillSubresourceSizes() -{ - META_FUNCTION_TASK(); - const Data::Size curr_subresource_raw_count = m_sub_resource_count.GetRawCount(); - const auto prev_subresource_raw_count = static_cast(m_sub_resource_sizes.size()); - if (curr_subresource_raw_count == prev_subresource_raw_count) - return; - - m_sub_resource_sizes.reserve(curr_subresource_raw_count); - for (Data::Index subresource_raw_index = prev_subresource_raw_count; subresource_raw_index < curr_subresource_raw_count; ++subresource_raw_index) - { - const SubResource::Index subresource_index(subresource_raw_index, m_sub_resource_count); - m_sub_resource_sizes.emplace_back(CalculateSubResourceDataSize(subresource_index)); - } -} - } // namespace Methane::Graphics::Base diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Sampler.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Sampler.cpp index 1bc62bdd0..ce5caf8fd 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Sampler.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Sampler.cpp @@ -34,9 +34,4 @@ Sampler::Sampler(const Context& context, const Settings& settings, , m_settings(settings) { } -void Sampler::SetData(const SubResources&, Rhi::ICommandQueue&) -{ - META_FUNCTION_NOT_IMPLEMENTED_DESCR("Samplers do not support setting the data."); -} - } // namespace Methane::Graphics::Base \ No newline at end of file diff --git a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Texture.cpp b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Texture.cpp index 14816a736..8a23ae5f1 100644 --- a/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Texture.cpp +++ b/Modules/Graphics/RHI/Base/Sources/Methane/Graphics/Base/Texture.cpp @@ -24,6 +24,7 @@ Base implementation of the texture interface. #include #include +#include #include #include #include @@ -35,6 +36,11 @@ Texture::Texture(const Context& context, const Settings& settings, State initial_state, Opt auto_transition_source_state_opt) : Resource(context, Rhi::IResource::Type::Texture, settings.usage_mask, initial_state, auto_transition_source_state_opt) , m_settings(settings) + , m_sub_resource_count( + settings.dimensions.GetDepth(), + settings.array_length, + settings.mipmapped ? GetRequiredMipLevelsCount(settings.dimensions) : 1U + ) { META_FUNCTION_TASK(); META_CHECK_ARG_NOT_EQUAL_DESCR(m_settings.usage_mask.GetValue(), 0U, "can not create texture with 'Unknown' usage mask"); @@ -42,13 +48,14 @@ Texture::Texture(const Context& context, const Settings& settings, META_CHECK_ARG_NOT_NULL_DESCR(m_settings.array_length, "array length should be greater than zero"); ValidateDimensions(m_settings.dimension_type, m_settings.dimensions, m_settings.mipmapped); - SetSubResourceCount( - SubResource::Count( - settings.dimensions.GetDepth(), - settings.array_length, - settings.mipmapped ? GetRequiredMipLevelsCount(settings.dimensions) : 1U - ) - ); + + const Data::Size subresource_raw_count = m_sub_resource_count.GetRawCount(); + m_sub_resource_sizes.reserve(subresource_raw_count); + for (Data::Index subresource_raw_index = 0U; subresource_raw_index < subresource_raw_count; ++subresource_raw_index) + { + const SubResource::Index subresource_index(subresource_raw_index, m_sub_resource_count); + m_sub_resource_sizes.emplace_back(CalculateSubResourceDataSize(subresource_index)); + } } void Texture::ValidateDimensions(DimensionType dimension_type, const Dimensions& dimensions, bool mipmapped) @@ -94,6 +101,33 @@ Data::Size Texture::GetDataSize(Data::MemoryState size_type) const noexcept : GetInitializedDataSize(); } +Data::Size Texture::GetSubResourceDataSize(const SubResource::Index& sub_resource_index) const +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_LESS(sub_resource_index, m_sub_resource_count); + return m_sub_resource_sizes[sub_resource_index.GetRawIndex(m_sub_resource_count)]; +} + +void Texture::SetData(Rhi::ICommandQueue&, const SubResources& sub_resources) +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_NOT_EMPTY_DESCR(sub_resources, "can not set buffer data from empty sub-resources"); + + Data::Size sub_resources_data_size = 0U; + for(const Rhi::SubResource& sub_resource : sub_resources) + { + META_CHECK_ARG_NAME_DESCR("sub_resource", !sub_resource.IsEmptyOrNull(), "can not set empty subresource data to buffer"); + sub_resources_data_size += sub_resource.GetDataSize(); + META_CHECK_ARG_LESS(sub_resource.GetIndex(), m_sub_resource_count); + } + + const Data::Size reserved_data_size = GetDataSize(Data::MemoryState::Reserved); + META_UNUSED(reserved_data_size); + + META_CHECK_ARG_LESS_OR_EQUAL_DESCR(sub_resources_data_size, reserved_data_size, "can not set more data than allocated buffer size"); + SetInitializedDataSize(sub_resources_data_size); +} + Data::Size Texture::CalculateSubResourceDataSize(const SubResource::Index& sub_resource_index) const { META_FUNCTION_TASK(); @@ -113,4 +147,38 @@ Data::Size Texture::CalculateSubResourceDataSize(const SubResource::Index& sub_r return pixel_size * mip_frame_size.GetPixelsCount(); } +void Texture::ValidateSubResource(const Rhi::SubResource& sub_resource) const +{ + META_FUNCTION_TASK(); + ValidateSubResource(sub_resource.GetIndex(), sub_resource.GetDataRangeOptional()); + + const Data::Index sub_resource_raw_index = sub_resource.GetIndex().GetRawIndex(m_sub_resource_count); + const Data::Size sub_resource_data_size = m_sub_resource_sizes[sub_resource_raw_index]; + META_UNUSED(sub_resource_data_size); + + if (sub_resource.HasDataRange()) + { + META_CHECK_ARG_EQUAL_DESCR(sub_resource.GetDataSize(), sub_resource.GetDataRange().GetLength(), + "sub-resource {} data size should be equal to the length of data range", sub_resource.GetIndex()); + } + META_CHECK_ARG_LESS_OR_EQUAL_DESCR(sub_resource.GetDataSize(), sub_resource_data_size, + "sub-resource {} data size should be less or equal than full resource size", sub_resource.GetIndex()); +} + +void Texture::ValidateSubResource(const SubResource::Index& sub_resource_index, const std::optional& sub_resource_data_range) const +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_LESS(sub_resource_index, m_sub_resource_count); + if (!sub_resource_data_range) + return; + + META_CHECK_ARG_NAME_DESCR("sub_resource_data_range", !sub_resource_data_range->IsEmpty(), "sub-resource {} data range can not be empty", sub_resource_index); + const Data::Index sub_resource_raw_index = sub_resource_index.GetRawIndex(m_sub_resource_count); + META_CHECK_ARG_LESS(sub_resource_raw_index, m_sub_resource_sizes.size()); + + const Data::Size sub_resource_data_size = m_sub_resource_sizes[sub_resource_raw_index]; + META_UNUSED(sub_resource_data_size); + META_CHECK_ARG_LESS_DESCR(sub_resource_data_range->GetEnd(), sub_resource_data_size + 1, "sub-resource index {}", sub_resource_index); +} + } // namespace Methane::Graphics::Base \ No newline at end of file diff --git a/Modules/Graphics/RHI/DirectX/CMakeLists.txt b/Modules/Graphics/RHI/DirectX/CMakeLists.txt index 331036276..7fc18eb9e 100644 --- a/Modules/Graphics/RHI/DirectX/CMakeLists.txt +++ b/Modules/Graphics/RHI/DirectX/CMakeLists.txt @@ -17,8 +17,10 @@ list(APPEND HEADERS ${INCLUDE_DIR}/ProgramArgumentBinding.h ${INCLUDE_DIR}/ProgramBindings.h ${INCLUDE_DIR}/RenderContext.h + ${INCLUDE_DIR}/ComputeContext.h ${INCLUDE_DIR}/RenderState.h ${INCLUDE_DIR}/ViewState.h + ${INCLUDE_DIR}/ComputeState.h ${INCLUDE_DIR}/IResource.h ${INCLUDE_DIR}/ResourceView.h ${INCLUDE_DIR}/Resource.hpp @@ -38,6 +40,7 @@ list(APPEND HEADERS ${INCLUDE_DIR}/ICommandList.h ${INCLUDE_DIR}/CommandList.hpp ${INCLUDE_DIR}/TransferCommandList.h + ${INCLUDE_DIR}/ComputeCommandList.h ${INCLUDE_DIR}/RenderCommandList.h ${INCLUDE_DIR}/ParallelRenderCommandList.h ) @@ -55,6 +58,7 @@ list(APPEND SOURCES ${SOURCES_DIR}/RenderContext.cpp ${SOURCES_DIR}/RenderState.cpp ${SOURCES_DIR}/ViewState.cpp + ${SOURCES_DIR}/ComputeState.cpp ${SOURCES_DIR}/IResource.cpp ${SOURCES_DIR}/ResourceView.cpp ${SOURCES_DIR}/ResourceBarriers.cpp @@ -71,6 +75,7 @@ list(APPEND SOURCES ${SOURCES_DIR}/CommandListSet.cpp ${SOURCES_DIR}/CommandListDebugGroup.cpp ${SOURCES_DIR}/TransferCommandList.cpp + ${SOURCES_DIR}/ComputeCommandList.cpp ${SOURCES_DIR}/RenderCommandList.cpp ${SOURCES_DIR}/ParallelRenderCommandList.cpp ) diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Buffer.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Buffer.h index 284df61bf..9737a05b6 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Buffer.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Buffer.h @@ -41,9 +41,9 @@ class Buffer final // NOSONAR - inheritance hierarchy is greater than 5 // IObject overrides bool SetName(std::string_view name) override; - // IResource overrides - void SetData(const SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) override; - SubResource GetData(const SubResource::Index& sub_resource_index = SubResource::Index(), const std::optional& data_range = {}) override; + // IBuffer overrides + void SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResource& sub_resource) override; + SubResource GetData(Rhi::ICommandQueue& target_cmd_queue, const BytesRangeOpt& data_range = {}) override; Opt InitializeNativeViewDescriptor(const View::Id& view_id) override; D3D12_VERTEX_BUFFER_VIEW GetNativeVertexBufferView() const; diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/CommandList.hpp b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/CommandList.hpp index 49cd27f3b..956014d32 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/CommandList.hpp +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/CommandList.hpp @@ -76,7 +76,7 @@ class CommandList SetCommandListState(Rhi::CommandListState::Encoding); } - // ICommandList interface + // Rhi::ICommandList interface void PushDebugGroup(Rhi::ICommandListDebugGroup& debug_group) final { @@ -123,7 +123,7 @@ class CommandList m_cp_command_list->ResourceBarrier(static_cast(d3d12_resource_barriers.size()), d3d12_resource_barriers.data()); } - // ICommandList interface + // Rhi::ICommandList interface void Reset(Rhi::ICommandListDebugGroup* debug_group_ptr) override { @@ -160,10 +160,11 @@ class CommandList return true; } - // ICommandList interface + // DirectX::ICommandList interface - CommandQueue& GetDirectCommandQueue() final { return static_cast(GetBaseCommandQueue()); } - ID3D12GraphicsCommandList& GetNativeCommandList() const final + CommandQueue& GetDirectCommandQueue() final { return static_cast(GetBaseCommandQueue()); } + Rhi::CommandListType GetCommandListType() const final { return Base::CommandList::GetType(); } + ID3D12GraphicsCommandList& GetNativeCommandList() const final { META_CHECK_ARG_NOT_NULL(m_cp_command_list); return *m_cp_command_list.Get(); diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/CommandQueue.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/CommandQueue.h index 946f07898..a9c10f1c5 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/CommandQueue.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/CommandQueue.h @@ -25,14 +25,15 @@ DirectX 12 implementation of the command queue interface. #include -#pragma warning(push) -#pragma warning(disable: 4189) -#include -#pragma warning(pop) - #include #include +#ifdef TRACY_ENABLE +#include +#else +struct TracyD3D12Ctx{}; +#endif + namespace Methane::Graphics::DirectX { @@ -51,6 +52,7 @@ class CommandQueue final // NOSONAR - destructor is needed // ICommandQueue interface [[nodiscard]] Ptr CreateFence() override; [[nodiscard]] Ptr CreateTransferCommandList() override; + [[nodiscard]] Ptr CreateComputeCommandList() override; [[nodiscard]] Ptr CreateRenderCommandList(Rhi::IRenderPass& render_pass) override; [[nodiscard]] Ptr CreateParallelRenderCommandList(Rhi::IRenderPass& render_pass) override; [[nodiscard]] Ptr CreateTimestampQueryPool(uint32_t max_timestamps_per_frame) override; diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ComputeCommandList.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ComputeCommandList.h new file mode 100644 index 000000000..fa37c9429 --- /dev/null +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ComputeCommandList.h @@ -0,0 +1,49 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/DirectX/ComputeCommandList.h +DirectX 12 implementation of the compute command list interface. + +******************************************************************************/ + +#pragma once + +#include "CommandList.hpp" + +#include + +namespace Methane::Graphics::DirectX +{ + +class ComputeCommandList final // NOSONAR - inheritance hierarchy depth is greater than 5 + : public CommandList +{ +public: + explicit ComputeCommandList(Base::CommandQueue& cmd_buffer); + + // IRenderCommandList interface + void Reset(IDebugGroup* debug_group_ptr = nullptr) override; + + // IComputeCommandList interface + void Dispatch(const Rhi::ThreadGroupsCount& thread_groups_count) override; + +private: + DescriptorHeap& m_gpu_shader_resources_descriptor_heap; +}; + +} // namespace Methane::Graphics::DirectX diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ComputeContext.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ComputeContext.h new file mode 100644 index 000000000..fcc8445ff --- /dev/null +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ComputeContext.h @@ -0,0 +1,40 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/DirectX/ComputeContext.h +DirectX implementation of the compute context interface. + +******************************************************************************/ + +#pragma once + +#include "Context.hpp" + +#include + +namespace Methane::Graphics::DirectX +{ + +class ComputeContext final // NOSONAR - inheritance hierarchy depth is greater than 5 + : public Context +{ +public: + using Context::Context; +}; + +} // namespace Methane::Graphics::DirectX diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ComputeState.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ComputeState.h new file mode 100644 index 000000000..4539778be --- /dev/null +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ComputeState.h @@ -0,0 +1,67 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/DirectX/ComputeState.h +DirectX 12 implementation of the render state interface. + +******************************************************************************/ + +#pragma once + +#include + +#include +#include + +namespace Methane::Graphics::DirectX +{ + +namespace wrl = Microsoft::WRL; + +class RenderContext; +class Program; +class Device; + +class ComputeState final + : public Base::ComputeState +{ +public: + ComputeState(const Rhi::IContext& context, const Settings& settings); + + // IComputeState interface + void Reset(const Settings& settings) override; + + // Base::ComputeState interface + void Apply(Base::ComputeCommandList& command_list) override; + + // IObject interface + bool SetName(std::string_view name) override; + + void InitializeNativePipelineState(); + wrl::ComPtr& GetNativePipelineState(); + +private: + Program& GetDirectProgram(); + const Device& GetDirectDevice() const { return m_device; } + + const Device& m_device; + D3D12_COMPUTE_PIPELINE_STATE_DESC m_pipeline_state_desc{ }; + wrl::ComPtr m_cp_pipeline_state; +}; + +} // namespace Methane::Graphics::DirectX diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Context.hpp b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Context.hpp index 23a7b4a41..f28456e5b 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Context.hpp +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Context.hpp @@ -25,9 +25,15 @@ DirectX 12 base template implementation of the context interface. #include "Fence.h" #include "Device.h" +#include "CommandQueue.h" +#include "Shader.h" +#include "Program.h" +#include "ComputeState.h" +#include "Buffer.h" +#include "Texture.h" +#include "Sampler.h" #include "System.h" #include "IContext.h" -#include "CommandQueue.h" #include "DescriptorManager.h" #include "ErrorHandling.h" @@ -92,10 +98,65 @@ class Context static_cast(Rhi::ISystem::Get()).ReportLiveObjects(); } - // IContext interface - const Device& GetDirectDevice() const noexcept final { return static_cast(Base::Context::GetBaseDevice()); } - CommandQueue& GetDirectDefaultCommandQueue(Rhi::CommandListType type) final { return static_cast(Base::Context::GetDefaultCommandKit(type).GetQueue()); } - DescriptorManager& GetDirectDescriptorManager() const noexcept final { return static_cast(Base::Context::GetDescriptorManager()); } + // IContext overrides + + [[nodiscard]] Ptr CreateCommandQueue(Rhi::CommandListType type) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, type); + } + + [[nodiscard]] Ptr CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(type, *this, settings); + } + + [[nodiscard]] Ptr CreateProgram(const Rhi::ProgramSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateComputeState(const Rhi::ComputeStateSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateBuffer(const Rhi::BufferSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateTexture(const Rhi::TextureSettings& settings) const override + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateSampler(const Rhi::SamplerSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + const Device& GetDirectDevice() const noexcept final + { + return static_cast(Base::Context::GetBaseDevice()); + } + + CommandQueue& GetDirectDefaultCommandQueue(Rhi::CommandListType type) final + { + META_FUNCTION_TASK(); + return static_cast(Base::Context::GetDefaultCommandKit(type).GetQueue()); + } + + DescriptorManager& GetDirectDescriptorManager() const noexcept final + { + return static_cast(Base::Context::GetDescriptorManager()); + } ID3D12QueryHeap& GetNativeQueryHeap(D3D12_QUERY_HEAP_TYPE type, uint32_t max_query_count = 1U << 15U) const final { @@ -115,7 +176,10 @@ class Context } protected: - Device& GetDirectMutableDevice() noexcept { return static_cast(GetBaseDevice()); } + Device& GetDirectMutableDevice() noexcept + { + return static_cast(GetBaseDevice()); + } private: using NativeQueryHeaps = std::array, D3D12_QUERY_HEAP_TYPE_COPY_QUEUE_TIMESTAMP + 1>; diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Device.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Device.h index df1510803..0ac5b77f0 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Device.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Device.h @@ -47,7 +47,8 @@ class Device final : public Base::Device Device(const wrl::ComPtr& cp_adapter, D3D_FEATURE_LEVEL feature_level, const Capabilities& capabilities); // IDevice interface - [[nodiscard]] Ptr CreateRenderContext(const Platform::AppEnvironment& env, tf::Executor& parallel_executor, const Rhi::RenderContextSettings& settings) override; + [[nodiscard]] Ptr CreateRenderContext(const Platform::AppEnvironment& env, tf::Executor& parallel_executor, const Rhi::RenderContextSettings& settings) override; + [[nodiscard]] Ptr CreateComputeContext(tf::Executor& parallel_executor, const Rhi::ComputeContextSettings& settings) override; // IObject interface bool SetName(std::string_view name) override; diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ErrorHandling.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ErrorHandling.h index fde6cbb57..f476ee771 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ErrorHandling.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ErrorHandling.h @@ -70,4 +70,4 @@ inline void ThrowIfFailed(HRESULT hr, const wrl::ComPtr& error_blob) throw RuntimeException(hr, error_blob); } -} // namespace Methane::Graphics +} // namespace Methane::Graphics::DirectX diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ICommandList.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ICommandList.h index 51c8593b6..65537b96d 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ICommandList.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ICommandList.h @@ -24,6 +24,7 @@ DirectX 12 command list interface and debug group implementation. #pragma once #include +#include #include @@ -38,6 +39,7 @@ struct ICommandList using DebugGroup = CommandListDebugGroup; virtual CommandQueue& GetDirectCommandQueue() = 0; + virtual Rhi::CommandListType GetCommandListType() const = 0; virtual ID3D12GraphicsCommandList& GetNativeCommandList() const = 0; virtual ID3D12GraphicsCommandList4* GetNativeCommandList4() const = 0; virtual void SetResourceBarriers(const Rhi::IResourceBarriers& resource_barriers) = 0; diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ProgramArgumentBinding.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ProgramArgumentBinding.h index 35c9e3c83..9f766e0e6 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ProgramArgumentBinding.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ProgramArgumentBinding.h @@ -45,6 +45,7 @@ enum class ProgramArgumentBindingType : uint32_t DescriptorTable = 0, ConstantBufferView, ShaderResourceView, + UnorderedAccessView, }; struct ProgramArgumentBindingSettings @@ -96,6 +97,7 @@ class ProgramArgumentBinding final // NOSONAR - custom destructor is required private: const Settings m_settings_dx; + const Rhi::ResourceUsageMask m_shader_usage; uint32_t m_root_parameter_index = std::numeric_limits::max();; DescriptorRange m_descriptor_range; const DescriptorHeapReservation* m_p_descriptor_heap_reservation = nullptr; diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ProgramBindings.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ProgramBindings.h index fde208afa..6b115e5fd 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ProgramBindings.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ProgramBindings.h @@ -60,7 +60,7 @@ class ProgramBindings final // NOSONAR - custom destructor is required void CompleteInitialization() override; void Apply(Base::CommandList& command_list, ApplyBehaviorMask apply_behavior) const override; - void Apply(ICommandList& command_list_dx, const Base::ProgramBindings* applied_program_bindings_ptr, ApplyBehaviorMask apply_behavior) const; + void Apply(ICommandList& command_list, const Base::ProgramBindings* applied_program_bindings_ptr, ApplyBehaviorMask apply_behavior) const; private: struct RootParameterBinding @@ -69,6 +69,9 @@ class ProgramBindings final // NOSONAR - custom destructor is required uint32_t root_parameter_index = 0U; D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor { }; D3D12_GPU_VIRTUAL_ADDRESS gpu_virtual_address = 0U; + + template + void Apply(ID3D12GraphicsCommandList& d3d12_command_list) const; }; template // function void(ArgumentBinding&, const DescriptorHeap::Reservation*) @@ -77,9 +80,12 @@ class ProgramBindings final // NOSONAR - custom destructor is required void AddRootParameterBinding(const Rhi::ProgramArgumentAccessor& argument_desc, const RootParameterBinding& root_parameter_binding); void UpdateRootParameterBindings(); void AddRootParameterBindingsForArgument(ArgumentBinding& argument_binding, const DescriptorHeap::Reservation* p_heap_reservation); + void ApplyRootParameterBindings(Rhi::ProgramArgumentAccessMask access, const ICommandList& command_list, + const Base::ProgramBindings* applied_program_bindings_ptr, bool apply_changes_only) const; + template void ApplyRootParameterBindings(Rhi::ProgramArgumentAccessMask access, ID3D12GraphicsCommandList& d3d12_command_list, const Base::ProgramBindings* applied_program_bindings_ptr, bool apply_changes_only) const; - void ApplyRootParameterBinding(const RootParameterBinding& root_parameter_binding, ID3D12GraphicsCommandList& d3d12_command_list) const; + void CopyDescriptorsToGpu() const; void CopyDescriptorsToGpuForArgument(const wrl::ComPtr& d3d12_device, ArgumentBinding& argument_binding, const DescriptorHeap::Reservation* p_heap_reservation) const; diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/QueryPool.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/QueryPool.h index c394efc55..6b4053b13 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/QueryPool.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/QueryPool.h @@ -74,12 +74,10 @@ class QueryPool : public Base::QueryPool CommandQueue& GetDirectCommandQueue() noexcept; const IContext& GetDirectContext() const noexcept { return m_context_dx; } IResource& GetDirectResultResource() const noexcept { return m_result_resource_dx; } + Rhi::IBuffer& GetResultBuffer() noexcept { return *m_result_buffer_ptr; } D3D12_QUERY_TYPE GetNativeQueryType() const noexcept { return m_native_query_type; } ID3D12QueryHeap& GetNativeQueryHeap() noexcept { return m_native_query_heap; } -protected: - Rhi::IBuffer& GetResultBuffer() noexcept { return *m_result_buffer_ptr; } - private: Ptr m_result_buffer_ptr; const IContext& m_context_dx; diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/RenderContext.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/RenderContext.h index 39cb2f55f..816f1d133 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/RenderContext.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/RenderContext.h @@ -41,12 +41,6 @@ class RenderContext final // NOSONAR - manual destructor is required, inheritanc const Rhi::RenderContextSettings& settings); // IContext interface - [[nodiscard]] Ptr CreateCommandQueue(Rhi::CommandListType type) const override; - [[nodiscard]] Ptr CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const override; - [[nodiscard]] Ptr CreateProgram(const Rhi::ProgramSettings& settings) const override; - [[nodiscard]] Ptr CreateBuffer(const Rhi::BufferSettings& settings) const override; - [[nodiscard]] Ptr CreateTexture(const Rhi::TextureSettings& settings) const override; - [[nodiscard]] Ptr CreateSampler(const Rhi::SamplerSettings& settings) const override; void WaitForGpu(WaitFor wait_for) override; // IRenderContext interface diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/RenderState.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/RenderState.h index 2b5f3cc6f..73015a274 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/RenderState.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/RenderState.h @@ -28,13 +28,6 @@ DirectX 12 implementation of the render state interface. #include #include -namespace Methane::Graphics::Base -{ - -class RenderCommandList; - -} // namespace Methane::Graphics::Base - namespace Methane::Graphics::DirectX { diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Resource.hpp b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Resource.hpp index af27cd846..ca9508c67 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Resource.hpp +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Resource.hpp @@ -25,9 +25,9 @@ DirectX 12 implementation of the resource interface. #include "IResource.h" #include "DescriptorHeap.h" -#include "RenderContext.h" #include "TransferCommandList.h" #include "Device.h" +#include "DescriptorManager.h" #include "ErrorHandling.h" #include @@ -65,9 +65,9 @@ class Resource // NOSONAR - destructor in use descriptor.heap.RemoveResource(descriptor.index); } - // Resource released callback has to be emitted before native resource is released try { + // Resource released callback has to be emitted before native resource is released Data::Emitter::Emit(&Rhi::IResourceCallback::OnResourceReleased, std::ref(*this)); } catch(const std::exception& e) @@ -113,13 +113,26 @@ class Resource // NOSONAR - destructor in use } // IResource overrides - ID3D12Resource& GetNativeResourceRef() const final { META_CHECK_ARG_NOT_NULL(m_cp_resource); return *m_cp_resource.Get(); } - ID3D12Resource* GetNativeResource() const noexcept final { return m_cp_resource.Get(); } - const wrl::ComPtr& GetNativeResourceComPtr() const noexcept final { return m_cp_resource; } - D3D12_GPU_VIRTUAL_ADDRESS GetNativeGpuAddress() const noexcept final { return m_cp_resource ? m_cp_resource->GetGPUVirtualAddress() : 0; } + ID3D12Resource& GetNativeResourceRef() const final { META_CHECK_ARG_NOT_NULL(m_cp_resource); return *m_cp_resource.Get(); } + ID3D12Resource* GetNativeResource() const noexcept final { return m_cp_resource.Get(); } + const wrl::ComPtr& GetNativeResourceComPtr() const noexcept final { return m_cp_resource; } + D3D12_GPU_VIRTUAL_ADDRESS GetNativeGpuAddress() const noexcept final { return m_cp_resource ? m_cp_resource->GetGPUVirtualAddress() : 0; } protected: + enum class TransferOperation + { + Upload, + Readback + }; + + struct TransferBarriers + { + Ptr sync_barriers_ptr; + Ptr begin_barriers_ptr; + }; + const IContext& GetDirectContext() const noexcept { return m_dx_context; } + void SetNativeResourceComPtr(const wrl::ComPtr& cp_resource) noexcept { m_cp_resource = cp_resource; } wrl::ComPtr CreateCommittedResource(const D3D12_RESOURCE_DESC& resource_desc, D3D12_HEAP_TYPE heap_type, D3D12_RESOURCE_STATES resource_state, const D3D12_CLEAR_VALUE* p_clear_value = nullptr) @@ -152,39 +165,33 @@ class Resource // NOSONAR - destructor in use SetState(resource_state); } - void InitializeFrameBufferResource(uint32_t frame_buffer_index) + TransferCommandList& PrepareResourceTransfer(TransferOperation transfer_operation, Rhi::ICommandQueue& target_cmd_queue, State transfer_state) { META_FUNCTION_TASK(); - META_CHECK_ARG_DESCR(m_cp_resource, !m_cp_resource, "committed resource is already initialized"); - ThrowIfFailed( - static_cast(GetDirectContext()).GetNativeSwapChain()->GetBuffer(frame_buffer_index, IID_PPV_ARGS(&m_cp_resource)), - GetDirectContext().GetDirectDevice().GetNativeDevice().Get() - ); - } + auto& transfer_cmd_list = dynamic_cast(GetContext().GetUploadCommandKit().GetListForEncoding()); + if (GetState() == transfer_state) + return transfer_cmd_list; - TransferCommandList& PrepareResourceUpload(Rhi::ICommandQueue& target_cmd_queue) - { - META_FUNCTION_TASK(); - auto& upload_cmd_list = dynamic_cast(GetContext().GetUploadCommandKit().GetListForEncoding()); - upload_cmd_list.RetainResource(*this); + TransferBarriers& transfer_barriers = transfer_operation == TransferOperation::Upload ? m_upload_barriers : m_read_back_barriers; + transfer_cmd_list.RetainResource(*this); // When upload command list has COPY type, before transitioning resource to CopyDest state prior copying, // first it has to be transitioned to Common state with synchronization command list of DIRECT type. // This is required due to DX12 limitation of using only copy-related resource barrier states in command lists of COPY type. - if (upload_cmd_list.GetNativeCommandList().GetType() == D3D12_COMMAND_LIST_TYPE_COPY && - SetState(State::Common, m_upload_sync_transition_barriers_ptr) && m_upload_sync_transition_barriers_ptr) + if (transfer_cmd_list.GetNativeCommandList().GetType() == D3D12_COMMAND_LIST_TYPE_COPY && + SetState(State::Common, transfer_barriers.sync_barriers_ptr) && transfer_barriers.sync_barriers_ptr) { Rhi::ICommandList& sync_cmd_list = GetContext().GetDefaultCommandKit(target_cmd_queue).GetListForEncoding( static_cast(Rhi::CommandListPurpose::PreUploadSync)); - sync_cmd_list.SetResourceBarriers(*m_upload_sync_transition_barriers_ptr); + sync_cmd_list.SetResourceBarriers(*transfer_barriers.sync_barriers_ptr); } - if (SetState(State::CopyDest, m_upload_begin_transition_barriers_ptr) && m_upload_begin_transition_barriers_ptr) + if (SetState(transfer_state, transfer_barriers.begin_barriers_ptr) && transfer_barriers.begin_barriers_ptr) { - upload_cmd_list.SetResourceBarriers(*m_upload_begin_transition_barriers_ptr); + transfer_cmd_list.SetResourceBarriers(*transfer_barriers.begin_barriers_ptr); } - return upload_cmd_list; + return transfer_cmd_list; } const IResource::Descriptor& GetDescriptorByViewId(const ResourceView::Id& view_id) @@ -220,8 +227,8 @@ class Resource // NOSONAR - destructor in use const IContext& m_dx_context; DescriptorByViewId m_descriptor_by_view_id; wrl::ComPtr m_cp_resource; - Ptr m_upload_sync_transition_barriers_ptr; - Ptr m_upload_begin_transition_barriers_ptr; + TransferBarriers m_upload_barriers; + TransferBarriers m_read_back_barriers; }; } // namespace Methane::Graphics::DirectX diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ResourceBarriers.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ResourceBarriers.h index 9e1518622..94e615d62 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ResourceBarriers.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/ResourceBarriers.h @@ -50,7 +50,7 @@ class ResourceBarriers final AddResult Add(const Barrier::Id& id, const Barrier& barrier) override; bool Remove(const Barrier::Id& id) override; - [[nodiscard]] const std::vector & GetNativeResourceBarriers() const + [[nodiscard]] const std::vector& GetNativeResourceBarriers() const { return m_native_resource_barriers; } private: diff --git a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Texture.h b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Texture.h index 4b0f9655a..97fb3f272 100644 --- a/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Texture.h +++ b/Modules/Graphics/RHI/DirectX/Include/Methane/Graphics/DirectX/Texture.h @@ -57,8 +57,11 @@ class Texture final // NOSONAR - inheritance hierarchy is greater than 5 // IObject overrides bool SetName(std::string_view name) override; - // IResource override - void SetData(const SubResources&, Rhi::ICommandQueue&) override; + // ITexture overrides + void SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResources& sub_resources) override; + SubResource GetData(Rhi::ICommandQueue& target_cmd_queue, + const SubResource::Index& sub_resource_index = {}, + const BytesRangeOpt& data_range = {}) override; // IResource override Opt InitializeNativeViewDescriptor(const View::Id& view_id) override; @@ -71,13 +74,15 @@ class Texture final // NOSONAR - inheritance hierarchy is greater than 5 void CreateShaderResourceView(const Descriptor& descriptor) const; void CreateShaderResourceView(const Descriptor& descriptor, const View::Id& view_id) const; + void CreateUnorderedAccessView(const Descriptor& descriptor, const View::Id& view_id) const; void CreateRenderTargetView(const Descriptor& descriptor) const; void CreateRenderTargetView(const Descriptor& descriptor, const View::Id& view_id) const; void CreateDepthStencilView(const Descriptor& descriptor) const; void GenerateMipLevels(std::vector& dx_sub_resources, ::DirectX::ScratchImage& scratch_image) const; - // Upload resource is created for TextureType::Image only + // Upload & Read-back resources are created for TextureType::Image only wrl::ComPtr m_cp_upload_resource; + wrl::ComPtr m_cp_read_back_resource; }; } // namespace Methane::Graphics::DirectX diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Buffer.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Buffer.cpp index 1bedec25a..87952bcf4 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Buffer.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Buffer.cpp @@ -21,6 +21,7 @@ DirectX 12 implementation of the buffer interface. ******************************************************************************/ +#include "Methane/Graphics/RHI/ResourceView.h" #include #include #include @@ -45,9 +46,14 @@ Buffer::Buffer(const Base::Context& context, const Settings& settings) const D3D12_HEAP_TYPE normal_heap_type = is_private_storage ? D3D12_HEAP_TYPE_DEFAULT : D3D12_HEAP_TYPE_UPLOAD; const D3D12_HEAP_TYPE heap_type = is_read_back_buffer ? D3D12_HEAP_TYPE_READBACK : normal_heap_type; const Rhi::ResourceState resource_state = is_read_back_buffer || is_private_storage - ? Rhi::ResourceState::CopyDest + ? Rhi::ResourceState::Common : Rhi::ResourceState::GenericRead; - const CD3DX12_RESOURCE_DESC resource_desc = CD3DX12_RESOURCE_DESC::Buffer(settings.size); + + D3D12_RESOURCE_FLAGS resource_flags = D3D12_RESOURCE_FLAG_NONE; + if (settings.usage_mask.HasAnyBit(Usage::ShaderWrite)) + resource_flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + + const CD3DX12_RESOURCE_DESC resource_desc = CD3DX12_RESOURCE_DESC::Buffer(settings.size, resource_flags); InitializeCommittedResource(resource_desc, heap_type, resource_state); @@ -74,83 +80,75 @@ bool Buffer::SetName(std::string_view name) return true; } -void Buffer::SetData(const SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) +void Buffer::SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResource& sub_resource) { META_FUNCTION_TASK(); - Resource::SetData(sub_resources, target_cmd_queue); + Resource::SetData(target_cmd_queue, sub_resource); const CD3DX12_RANGE zero_read_range(0U, 0U); - const bool is_private_storage = GetSettings().storage_mode == IBuffer::StorageMode::Private; - ID3D12Resource & d3d12_resource = is_private_storage ? *m_cp_upload_resource.Get() : GetNativeResourceRef(); - for (const SubResource& sub_resource: sub_resources) + const bool is_private_storage = GetSettings().storage_mode == IBuffer::StorageMode::Private; + ID3D12Resource& d3d12_resource = is_private_storage ? *m_cp_upload_resource.Get() : GetNativeResourceRef(); + + // Using zero range, since we're not going to read this resource on CPU + const Data::Index sub_resource_raw_index = sub_resource.GetIndex().GetRawIndex(Rhi::SubResourceCount()); + Data::RawPtr p_sub_resource_data = nullptr; + ThrowIfFailed( + d3d12_resource.Map(sub_resource_raw_index, &zero_read_range, + reinterpret_cast(&p_sub_resource_data)), // NOSONAR + GetDirectContext().GetDirectDevice().GetNativeDevice().Get() + ); + + META_CHECK_ARG_NOT_NULL_DESCR(p_sub_resource_data, "failed to map buffer subresource"); + stdext::checked_array_iterator target_data_it(p_sub_resource_data, sub_resource.GetDataSize()); + std::copy(sub_resource.GetDataPtr(), sub_resource.GetDataEndPtr(), target_data_it); + + if (sub_resource.HasDataRange()) + { + const CD3DX12_RANGE write_range(sub_resource.GetDataRange().GetStart(), sub_resource.GetDataRange().GetEnd()); + d3d12_resource.Unmap(sub_resource_raw_index, &write_range); + } + else { - ValidateSubResource(sub_resource); - - // Using zero range, since we're not going to read this resource on CPU - const Data::Index sub_resource_raw_index = sub_resource.GetIndex().GetRawIndex(GetSubresourceCount()); - Data::RawPtr p_sub_resource_data = nullptr; - ThrowIfFailed( - d3d12_resource.Map(sub_resource_raw_index, &zero_read_range, - reinterpret_cast(&p_sub_resource_data)), // NOSONAR - GetDirectContext().GetDirectDevice().GetNativeDevice().Get() - ); - - META_CHECK_ARG_NOT_NULL_DESCR(p_sub_resource_data, "failed to map buffer subresource"); - stdext::checked_array_iterator target_data_it(p_sub_resource_data, sub_resource.GetDataSize()); - std::copy(sub_resource.GetDataPtr(), sub_resource.GetDataEndPtr(), target_data_it); - - if (sub_resource.HasDataRange()) - { - const CD3DX12_RANGE write_range(sub_resource.GetDataRange().GetStart(), sub_resource.GetDataRange().GetEnd()); - d3d12_resource.Unmap(sub_resource_raw_index, &write_range); - } - else - { - d3d12_resource.Unmap(sub_resource_raw_index, nullptr); - } + d3d12_resource.Unmap(sub_resource_raw_index, nullptr); } if (!is_private_storage) return; // In case of private GPU storage, copy buffer data from intermediate upload resource to the private GPU resource - const TransferCommandList& upload_cmd_list = PrepareResourceUpload(target_cmd_queue); + const TransferCommandList& upload_cmd_list = PrepareResourceTransfer(TransferOperation::Upload, target_cmd_queue, State::CopyDest); upload_cmd_list.GetNativeCommandList().CopyResource(GetNativeResource(), m_cp_upload_resource.Get()); GetContext().RequestDeferredAction(Rhi::IContext::DeferredAction::UploadResources); } -Rhi::SubResource Buffer::GetData(const SubResource::Index& sub_resource_index, const std::optional& data_range) +Rhi::SubResource Buffer::GetData(Rhi::ICommandQueue&, const BytesRangeOpt& data_range) { META_FUNCTION_TASK(); META_CHECK_ARG_TRUE_DESCR(GetUsage().HasAnyBit(Rhi::ResourceUsage::ReadBack), "getting buffer data from GPU is allowed for buffers with CPU Read-back flag only"); - ValidateSubResource(sub_resource_index, data_range); - - const Data::Index sub_resource_raw_index = sub_resource_index.GetRawIndex(GetSubresourceCount()); - const Data::Index data_start = data_range ? data_range->GetStart() : 0U; - const Data::Index data_length = data_range ? data_range->GetLength() : GetSubResourceDataSize(sub_resource_index); - const Data::Index data_end = data_start + data_length; + const Data::Index data_start = data_range ? data_range->GetStart() : 0U; + const Data::Index data_length = data_range ? data_range->GetLength() : GetDataSize(); + const Data::Index data_end = data_start + data_length; ID3D12Resource& d3d12_resource = GetNativeResourceRef(); const CD3DX12_RANGE read_range(data_start, data_start + data_length); - Data::RawPtr p_sub_resource_data = nullptr; + Data::RawPtr sub_resource_data_ptr = nullptr; ThrowIfFailed( - d3d12_resource.Map(sub_resource_raw_index, &read_range, - reinterpret_cast(&p_sub_resource_data)), // NOSONAR + d3d12_resource.Map(0U, &read_range, reinterpret_cast(&sub_resource_data_ptr)), // NOSONAR GetDirectContext().GetDirectDevice().GetNativeDevice().Get() ); - META_CHECK_ARG_NOT_NULL_DESCR(p_sub_resource_data, "failed to map buffer subresource"); + META_CHECK_ARG_NOT_NULL_DESCR(sub_resource_data_ptr, "failed to map buffer subresource"); - stdext::checked_array_iterator source_data_it(p_sub_resource_data, data_end); + stdext::checked_array_iterator source_data_it(sub_resource_data_ptr, data_end); Data::Bytes sub_resource_data(data_length, {}); std::copy(source_data_it + data_start, source_data_it + data_end, sub_resource_data.begin()); const CD3DX12_RANGE zero_write_range(0, 0); - d3d12_resource.Unmap(sub_resource_raw_index, &zero_write_range); + d3d12_resource.Unmap(0U, &zero_write_range); - return SubResource(std::move(sub_resource_data), sub_resource_index, data_range); + return SubResource(std::move(sub_resource_data), Rhi::SubResourceIndex(), data_range); } D3D12_VERTEX_BUFFER_VIEW Buffer::GetNativeVertexBufferView() const diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/CommandListSet.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/CommandListSet.cpp index 8e01659d5..748ac52a8 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/CommandListSet.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/CommandListSet.cpp @@ -49,7 +49,7 @@ CommandListSet::CommandListSet(const Refs& command_list_refs, const Refs& base_command_list_refs = GetBaseRefs(); std::stringstream fence_name_ss; - fence_name_ss << "Execution completed for command list set: "; + fence_name_ss << "Execution completed for command list set:"; m_native_command_lists.reserve(base_command_list_refs.size()); for(const Ref& command_list_ref : base_command_list_refs) diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/CommandQueue.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/CommandQueue.cpp index 01bab3799..032f6ca96 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/CommandQueue.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/CommandQueue.cpp @@ -25,6 +25,7 @@ DirectX 12 implementation of the command queue interface. #include #include #include +#include #include #include #include @@ -70,6 +71,9 @@ static D3D12_COMMAND_LIST_TYPE GetNativeCommandListType(Rhi::CommandListType com case Rhi::CommandListType::ParallelRender: return D3D12_COMMAND_LIST_TYPE_DIRECT; + case Rhi::CommandListType::Compute: + return D3D12_COMMAND_LIST_TYPE_COMPUTE; + default: META_UNEXPECTED_ARG_RETURN(command_list_type, D3D12_COMMAND_LIST_TYPE_DIRECT); } @@ -122,6 +126,12 @@ Ptr CommandQueue::CreateTransferCommandList() return std::make_shared(*this); } +Ptr CommandQueue::CreateComputeCommandList() +{ + META_FUNCTION_TASK(); + return std::make_shared(*this); +} + Ptr CommandQueue::CreateRenderCommandList(Rhi::IRenderPass& render_pass) { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ComputeCommandList.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ComputeCommandList.cpp new file mode 100644 index 000000000..b041512d4 --- /dev/null +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ComputeCommandList.cpp @@ -0,0 +1,60 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/DirectX/ComputeCommandList.cpp +DirectX 12 implementation of the transfer command list interface. + +******************************************************************************/ + +#include "Methane/Graphics/Base/ComputeCommandList.h" +#include +#include + +#include +#include +#include + +namespace Methane::Graphics::DirectX +{ + +ComputeCommandList::ComputeCommandList(Base::CommandQueue& cmd_queue) + : CommandList(D3D12_COMMAND_LIST_TYPE_COMPUTE, cmd_queue) + , m_gpu_shader_resources_descriptor_heap(GetDirectCommandQueue().GetDirectContext().GetDirectDescriptorManager() + .GetDefaultShaderVisibleDescriptorHeap(DescriptorHeap::Type::ShaderResources)) +{ } + +void ComputeCommandList::Reset(IDebugGroup* debug_group_ptr) +{ + META_FUNCTION_TASK(); + CommandList::Reset(debug_group_ptr); + + // Set GPU Visible Shader Resources descriptor heap to allow descriptor table resources binding + ID3D12GraphicsCommandList& dx_command_list = GetNativeCommandListRef(); + ID3D12DescriptorHeap* d3d12_shader_resources_heap = m_gpu_shader_resources_descriptor_heap.GetNativeDescriptorHeap(); + dx_command_list.SetDescriptorHeaps(1U, &d3d12_shader_resources_heap); +} + +void ComputeCommandList::Dispatch(const Rhi::ThreadGroupsCount& thread_groups_count) +{ + META_FUNCTION_TASK(); + Base::ComputeCommandList::Dispatch(thread_groups_count); + ID3D12GraphicsCommandList& dx_command_list = GetNativeCommandListRef(); + dx_command_list.Dispatch(thread_groups_count.GetWidth(), thread_groups_count.GetHeight(), thread_groups_count.GetDepth()); +} + +} // namespace Methane::Graphics::DirectX diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ComputeState.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ComputeState.cpp new file mode 100644 index 000000000..26ce07704 --- /dev/null +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ComputeState.cpp @@ -0,0 +1,142 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/DirectX/ComputeState.cpp +DirectX 12 implementation of the render state interface. + +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace Methane::Graphics::DirectX +{ + +static const Device& GetDirectDeviceFromContext(const Rhi::IContext& context) +{ + META_FUNCTION_TASK(); + switch (context.GetType()) + { + case Rhi::ContextType::Render: + return dynamic_cast(context).GetDirectDevice(); + + case Rhi::ContextType::Compute: + return dynamic_cast(context).GetDirectDevice(); + + default: + META_UNEXPECTED_ARG_DESCR(context.GetType(), "Unexpected context type"); + } +} + +[[nodiscard]] +inline CD3DX12_SHADER_BYTECODE GetShaderByteCode(const Ptr& shader_ptr) +{ + META_FUNCTION_TASK(); + const Data::Chunk* p_byte_code_chunk = shader_ptr ? static_cast(*shader_ptr).GetNativeByteCode() : nullptr; + return p_byte_code_chunk + ? CD3DX12_SHADER_BYTECODE(p_byte_code_chunk->GetDataPtr(), p_byte_code_chunk->GetDataSize()) + : CD3DX12_SHADER_BYTECODE(nullptr, 0); +} + +ComputeState::ComputeState(const Rhi::IContext& context, const Settings& settings) + : Base::ComputeState(context, settings) + , m_device(GetDirectDeviceFromContext(context)) +{ + META_FUNCTION_TASK(); + Reset(settings); // NOSONAR - method is not overridable in final class +} + +void ComputeState::Reset(const Settings& settings) +{ + META_FUNCTION_TASK(); + Base::ComputeState::Reset(settings); + + // Set pipeline state descriptor for program + const Program& dx_program = GetDirectProgram(); + m_pipeline_state_desc.pRootSignature = dx_program.GetNativeRootSignature().Get(); + m_pipeline_state_desc.CS = GetShaderByteCode(dx_program.GetShader(Rhi::ShaderType::Compute)); + m_pipeline_state_desc.NodeMask = {}; + m_pipeline_state_desc.CachedPSO = {}; + m_pipeline_state_desc.Flags = {}; + + m_cp_pipeline_state.Reset(); +} + +void ComputeState::Apply(Base::ComputeCommandList& command_list) +{ + META_FUNCTION_TASK(); + const auto& dx_compute_command_list = static_cast(command_list); + ID3D12GraphicsCommandList& d3d12_command_list = dx_compute_command_list.GetNativeCommandList(); + + d3d12_command_list.SetPipelineState(GetNativePipelineState().Get()); + d3d12_command_list.SetComputeRootSignature(GetDirectProgram().GetNativeRootSignature().Get()); +} + +bool ComputeState::SetName(std::string_view name) +{ + META_FUNCTION_TASK(); + if (!Base::ComputeState::SetName(name)) + return false; + + if (m_cp_pipeline_state) + { + m_cp_pipeline_state->SetName(nowide::widen(name).c_str()); + } + return true; +} + +void ComputeState::InitializeNativePipelineState() +{ + META_FUNCTION_TASK(); + if (m_cp_pipeline_state) + return; + + const wrl::ComPtr& cp_native_device = GetDirectDevice().GetNativeDevice(); + ThrowIfFailed(cp_native_device->CreateComputePipelineState(&m_pipeline_state_desc, IID_PPV_ARGS(&m_cp_pipeline_state)), cp_native_device.Get()); + SetName(GetName()); +} + +wrl::ComPtr& ComputeState::GetNativePipelineState() +{ + META_FUNCTION_TASK(); + if (!m_cp_pipeline_state) + { + InitializeNativePipelineState(); + } + return m_cp_pipeline_state; +} + +Program& ComputeState::GetDirectProgram() +{ + META_FUNCTION_TASK(); + return static_cast(GetProgram()); +} + +} // namespace Methane::Graphics::DirectX diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Device.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Device.cpp index 709bb92dd..01b8a80d3 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Device.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Device.cpp @@ -24,6 +24,7 @@ DirectX 12 implementation of the device interface. #include #include +#include #include #include @@ -117,6 +118,14 @@ Ptr Device::CreateRenderContext(const Platform::AppEnvironm return render_context_ptr; } +Ptr Device::CreateComputeContext(tf::Executor& parallel_executor, const Rhi::ComputeContextSettings& settings) +{ + META_FUNCTION_TASK(); + const auto compute_context_ptr = std::make_shared(*this, parallel_executor, settings); + compute_context_ptr->Initialize(*this, true); + return compute_context_ptr; +} + bool Device::SetName(std::string_view name) { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Program.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Program.cpp index f57c278f8..9b236943f 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Program.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Program.cpp @@ -87,10 +87,11 @@ static D3D12_SHADER_VISIBILITY GetShaderVisibilityByType(Rhi::ShaderType shader_ META_FUNCTION_TASK(); switch (shader_type) { - case Rhi::ShaderType::All: return D3D12_SHADER_VISIBILITY_ALL; - case Rhi::ShaderType::Vertex: return D3D12_SHADER_VISIBILITY_VERTEX; - case Rhi::ShaderType::Pixel: return D3D12_SHADER_VISIBILITY_PIXEL; - default: META_UNEXPECTED_ARG_RETURN(shader_type, D3D12_SHADER_VISIBILITY_ALL); + case Rhi::ShaderType::All: return D3D12_SHADER_VISIBILITY_ALL; + case Rhi::ShaderType::Vertex: return D3D12_SHADER_VISIBILITY_VERTEX; + case Rhi::ShaderType::Pixel: return D3D12_SHADER_VISIBILITY_PIXEL; + case Rhi::ShaderType::Compute: return D3D12_SHADER_VISIBILITY_ALL; + default: META_UNEXPECTED_ARG_RETURN(shader_type, D3D12_SHADER_VISIBILITY_ALL); } }; @@ -196,6 +197,10 @@ void Program::InitRootSignature() root_parameters.back().InitAsShaderResourceView(bind_settings.point, bind_settings.space, D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC, shader_visibility); break; + case DirectArgumentBinding::Type::UnorderedAccessView: + root_parameters.back().InitAsUnorderedAccessView(bind_settings.point, bind_settings.space, D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC, shader_visibility); + break; + default: META_UNEXPECTED_ARG(bind_settings.type); } diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ProgramArgumentBinding.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ProgramArgumentBinding.cpp index e1e469153..c68a34521 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ProgramArgumentBinding.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ProgramArgumentBinding.cpp @@ -32,9 +32,21 @@ DirectX 12 implementation of the program argument binding interface. namespace Methane::Graphics::DirectX { +static Rhi::ResourceUsageMask GetShaderUsage(ProgramArgumentBindingType binding_type) noexcept +{ + META_FUNCTION_TASK(); + Rhi::ResourceUsageMask shader_usage(Rhi::ResourceUsage::ShaderRead); + if (binding_type == ProgramArgumentBindingType::UnorderedAccessView) + { + shader_usage.SetBitOn(Rhi::ResourceUsage::ShaderWrite); + } + return shader_usage; +} + ProgramArgumentBinding::ProgramArgumentBinding(const Base::Context& context, const Settings& settings) : Base::ProgramArgumentBinding(context, settings) , m_settings_dx(settings) + , m_shader_usage(GetShaderUsage(settings.type)) , m_cp_native_device(dynamic_cast(context).GetDirectDevice().GetNativeDevice()) { META_FUNCTION_TASK(); @@ -45,6 +57,7 @@ ProgramArgumentBinding::ProgramArgumentBinding(const Base::Context& context, con ProgramArgumentBinding::ProgramArgumentBinding(const ProgramArgumentBinding& other) : Base::ProgramArgumentBinding(other) , m_settings_dx(other.m_settings_dx) + , m_shader_usage(other.m_shader_usage) , m_root_parameter_index(other.m_root_parameter_index) , m_descriptor_range(other.m_descriptor_range) , m_p_descriptor_heap_reservation(other.m_p_descriptor_heap_reservation) @@ -88,23 +101,23 @@ bool ProgramArgumentBinding::SetResourceViews(const Rhi::ResourceViews& resource const uint32_t descriptor_range_start = m_p_descriptor_heap_reservation ? m_p_descriptor_heap_reservation->GetRange(m_settings_dx.argument.GetAccessorIndex()).GetStart() : std::numeric_limits::max(); - const DescriptorHeap* p_dx_descriptor_heap = m_p_descriptor_heap_reservation + const DescriptorHeap* p_dx_descriptor_heap = m_p_descriptor_heap_reservation ? static_cast(&m_p_descriptor_heap_reservation->heap.get()) : nullptr; - const DescriptorHeap::Type descriptor_heap_type = p_dx_descriptor_heap + const DescriptorHeap::Type descriptor_heap_type = p_dx_descriptor_heap ? p_dx_descriptor_heap->GetSettings().type : DescriptorHeap::Type::Undefined; const D3D12_DESCRIPTOR_HEAP_TYPE native_heap_type = p_dx_descriptor_heap ? p_dx_descriptor_heap->GetNativeDescriptorHeapType() : D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - constexpr Rhi::ResourceUsageMask shader_read_usage(Rhi::ResourceUsage::ShaderRead); uint32_t resource_index = 0; m_resource_views_dx.clear(); m_resource_views_dx.reserve(resource_views.size()); - for(const Rhi::IResource::View& resource_view : resource_views) + + for(const Rhi::ResourceView& resource_view : resource_views) { - m_resource_views_dx.emplace_back(resource_view, shader_read_usage); + m_resource_views_dx.emplace_back(resource_view, m_shader_usage); if (!p_dx_descriptor_heap) continue; diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ProgramBindings.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ProgramBindings.cpp index c5013a44c..c843de50a 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ProgramBindings.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/ProgramBindings.cpp @@ -128,7 +128,7 @@ void ProgramBindings::Apply(Base::CommandList& command_list, ApplyBehaviorMask a Apply(dynamic_cast(command_list), command_list.GetProgramBindingsPtr(), apply_behavior); } -void ProgramBindings::Apply(ICommandList& command_list_dx, const Base::ProgramBindings* applied_program_bindings_ptr, ApplyBehaviorMask apply_behavior) const +void ProgramBindings::Apply(ICommandList& command_list, const Base::ProgramBindings* applied_program_bindings_ptr, ApplyBehaviorMask apply_behavior) const { META_FUNCTION_TASK(); Rhi::ProgramArgumentAccessMask apply_access_mask; @@ -143,12 +143,11 @@ void ProgramBindings::Apply(ICommandList& command_list_dx, const Base::ProgramBi // Set resource transition barriers before applying resource bindings if (apply_behavior.HasAnyBit(ApplyBehavior::StateBarriers)) { - ApplyResourceTransitionBarriers(command_list_dx, apply_access_mask); + ApplyResourceTransitionBarriers(command_list, apply_access_mask); } // Apply root parameter bindings after resource barriers - ID3D12GraphicsCommandList& d3d12_command_list = command_list_dx.GetNativeCommandList(); - ApplyRootParameterBindings(apply_access_mask, d3d12_command_list, applied_program_bindings_ptr, + ApplyRootParameterBindings(apply_access_mask, command_list, applied_program_bindings_ptr, apply_behavior.HasAnyBit(ApplyBehavior::ChangesOnly)); } @@ -292,8 +291,7 @@ void ProgramBindings::AddRootParameterBindingsForArgument(ArgumentBinding& argum for (const ResourceView& resource_view_dx : argument_binding.GetDirectResourceViews()) { - if (binding_settings.type == DXBindingType::ConstantBufferView || - binding_settings.type == DXBindingType::ShaderResourceView) + if (binding_settings.type != DXBindingType::DescriptorTable) { AddRootParameterBinding(binding_settings.argument, { argument_binding, @@ -305,6 +303,28 @@ void ProgramBindings::AddRootParameterBindingsForArgument(ArgumentBinding& argum } } +void ProgramBindings::ApplyRootParameterBindings(Rhi::ProgramArgumentAccessMask access, const ICommandList& command_list, + const Base::ProgramBindings* applied_program_bindings_ptr, bool apply_changes_only) const +{ + META_FUNCTION_TASK(); + ID3D12GraphicsCommandList& d3d12_command_list = command_list.GetNativeCommandList(); + switch(Rhi::CommandListType command_list_type = command_list.GetCommandListType(); + command_list_type) + { + case Rhi::CommandListType::Render: + ApplyRootParameterBindings(access, d3d12_command_list, applied_program_bindings_ptr, apply_changes_only); + break; + + case Rhi::CommandListType::Compute: + ApplyRootParameterBindings(access, d3d12_command_list, applied_program_bindings_ptr, apply_changes_only); + break; + + default: + META_UNEXPECTED_ARG(command_list_type); + } +} + +template void ProgramBindings::ApplyRootParameterBindings(Rhi::ProgramArgumentAccessMask access, ID3D12GraphicsCommandList& d3d12_command_list, const Base::ProgramBindings* applied_program_bindings_ptr, bool apply_changes_only) const { @@ -312,35 +332,52 @@ void ProgramBindings::ApplyRootParameterBindings(Rhi::ProgramArgumentAccessMask Data::ForEachBitInEnumMask(access, [this, &d3d12_command_list, applied_program_bindings_ptr, apply_changes_only](Rhi::ProgramArgumentAccessType access_type) { - const bool do_program_bindings_comparing = access_type == Rhi::ProgramArgumentAccessType::Mutable && apply_changes_only && applied_program_bindings_ptr; - const RootParameterBindings& root_parameter_bindings = m_root_parameter_bindings_by_access[magic_enum::enum_index(access_type).value()]; + const bool do_program_bindings_comparing = access_type == Rhi::ProgramArgumentAccessType::Mutable && apply_changes_only && applied_program_bindings_ptr; + const RootParameterBindings& root_parameter_bindings = m_root_parameter_bindings_by_access[magic_enum::enum_index(access_type).value()]; - for (const RootParameterBinding& root_parameter_binding : root_parameter_bindings) - { - if (do_program_bindings_comparing && root_parameter_binding.argument_binding.IsAlreadyApplied(GetProgram(), *applied_program_bindings_ptr)) - continue; + for (const RootParameterBinding& root_parameter_binding : root_parameter_bindings) + { + if (do_program_bindings_comparing && root_parameter_binding.argument_binding.IsAlreadyApplied(GetProgram(), *applied_program_bindings_ptr)) + continue; - ApplyRootParameterBinding(root_parameter_binding, d3d12_command_list); - } + root_parameter_binding.Apply(d3d12_command_list); + } }); } -void ProgramBindings::ApplyRootParameterBinding(const RootParameterBinding& root_parameter_binding, ID3D12GraphicsCommandList& d3d12_command_list) const +template +void ProgramBindings::RootParameterBinding::Apply(ID3D12GraphicsCommandList& d3d12_command_list) const { META_FUNCTION_TASK(); - switch (const ArgumentBinding::Type binding_type = root_parameter_binding.argument_binding.GetDirectSettings().type; + switch (const ArgumentBinding::Type binding_type = argument_binding.GetDirectSettings().type; binding_type) { case ArgumentBinding::Type::DescriptorTable: - d3d12_command_list.SetGraphicsRootDescriptorTable(root_parameter_binding.root_parameter_index, root_parameter_binding.base_descriptor); + if constexpr (command_list_type == Rhi::CommandListType::Render) + d3d12_command_list.SetGraphicsRootDescriptorTable(root_parameter_index, base_descriptor); + else if constexpr (command_list_type == Rhi::CommandListType::Compute) + d3d12_command_list.SetComputeRootDescriptorTable(root_parameter_index, base_descriptor); break; case ArgumentBinding::Type::ConstantBufferView: - d3d12_command_list.SetGraphicsRootConstantBufferView(root_parameter_binding.root_parameter_index, root_parameter_binding.gpu_virtual_address); + if constexpr (command_list_type == Rhi::CommandListType::Render) + d3d12_command_list.SetGraphicsRootConstantBufferView(root_parameter_index, gpu_virtual_address); + else if constexpr (command_list_type == Rhi::CommandListType::Compute) + d3d12_command_list.SetComputeRootConstantBufferView(root_parameter_index, gpu_virtual_address); break; case ArgumentBinding::Type::ShaderResourceView: - d3d12_command_list.SetGraphicsRootShaderResourceView(root_parameter_binding.root_parameter_index, root_parameter_binding.gpu_virtual_address); + if constexpr (command_list_type == Rhi::CommandListType::Render) + d3d12_command_list.SetGraphicsRootShaderResourceView(root_parameter_index, gpu_virtual_address); + else if constexpr (command_list_type == Rhi::CommandListType::Compute) + d3d12_command_list.SetComputeRootShaderResourceView(root_parameter_index, gpu_virtual_address); + break; + + case ArgumentBinding::Type::UnorderedAccessView: + if constexpr (command_list_type == Rhi::CommandListType::Render) + d3d12_command_list.SetGraphicsRootUnorderedAccessView(root_parameter_index, gpu_virtual_address); + else if constexpr (command_list_type == Rhi::CommandListType::Compute) + d3d12_command_list.SetComputeRootUnorderedAccessView(root_parameter_index, gpu_virtual_address); break; default: diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/QueryPool.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/QueryPool.cpp index c85722bff..7b4ac2faf 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/QueryPool.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/QueryPool.cpp @@ -126,7 +126,7 @@ Rhi::IResource::SubResource Query::GetData() const META_FUNCTION_TASK(); META_CHECK_ARG_EQUAL_DESCR(GetCommandList().GetState(), Base::CommandList::State::Pending, "query data can be retrieved only when command list is in Pending/Completed state"); META_CHECK_ARG_EQUAL_DESCR(GetState(), Rhi::IQuery::State::Resolved, "query data can not be retrieved for unresolved query"); - return GetDirectQueryPool().GetDirectResultResource().GetData(Rhi::IResource::SubResource::Index(), GetDataRange()); + return GetDirectQueryPool().GetResultBuffer().GetData(GetCommandList().GetCommandQueue(), GetDataRange()); } QueryPool& Query::GetDirectQueryPool() const noexcept diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/RenderContext.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/RenderContext.cpp index eb5edbb55..28424c00d 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/RenderContext.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/RenderContext.cpp @@ -24,14 +24,9 @@ DirectX 12 implementation of the render context interface. #include #include #include -#include -#include #include #include #include -#include -#include -#include #include #include @@ -64,42 +59,6 @@ RenderContext::RenderContext(const Platform::AppEnvironment& env, Base::Device& , m_platform_env(env) { } -Ptr RenderContext::CreateCommandQueue(Rhi::CommandListType type) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, type); -} - -Ptr RenderContext::CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(type, *this, settings); -} - -Ptr RenderContext::CreateProgram(const Rhi::ProgramSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateBuffer(const Rhi::BufferSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateTexture(const Rhi::TextureSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateSampler(const Rhi::SamplerSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - Ptr RenderContext::CreateRenderState(const Rhi::RenderStateSettings& settings) const { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Shader.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Shader.cpp index e0ad96b5d..51a822179 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Shader.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Shader.cpp @@ -49,21 +49,58 @@ namespace Methane::Graphics::DirectX static const std::set> g_skip_semantic_names{{ "SV_VERTEXID", "SV_INSTANCEID", "SV_ISFRONTFACE" }}; +static Rhi::IResource::Type GetResourceTypeByDimensionType(D3D_SRV_DIMENSION dimension_type) +{ + META_FUNCTION_TASK(); + switch (dimension_type) + { + case D3D_SRV_DIMENSION_BUFFER: + case D3D_SRV_DIMENSION_BUFFEREX: + return Rhi::IResource::Type::Buffer; + + case D3D_SRV_DIMENSION_TEXTURE1D: + case D3D_SRV_DIMENSION_TEXTURE1DARRAY: + case D3D_SRV_DIMENSION_TEXTURE2D: + case D3D_SRV_DIMENSION_TEXTURE2DARRAY: + case D3D_SRV_DIMENSION_TEXTURE2DMS: + case D3D_SRV_DIMENSION_TEXTURE2DMSARRAY: + case D3D_SRV_DIMENSION_TEXTURE3D: + case D3D_SRV_DIMENSION_TEXTURECUBE: + case D3D_SRV_DIMENSION_TEXTURECUBEARRAY: + return Rhi::IResource::Type::Texture; + + default: META_UNEXPECTED_ARG_DESCR_RETURN(dimension_type, Rhi::IResource::Type::Buffer, "unable to determine resource type by DX resource dimension type"); + } +} + [[nodiscard]] -static Rhi::IResource::Type GetResourceTypeByInputType(D3D_SHADER_INPUT_TYPE input_type) +static Rhi::IResource::Type GetResourceTypeByInputAndDimensionType(D3D_SHADER_INPUT_TYPE input_type, D3D_SRV_DIMENSION dimension_type) { META_FUNCTION_TASK(); switch (input_type) { - case D3D_SIT_CBUFFER: case D3D_SIT_STRUCTURED: - case D3D_SIT_TBUFFER: return Rhi::IResource::Type::Buffer; - case D3D_SIT_TEXTURE: return Rhi::IResource::Type::Texture; - case D3D_SIT_SAMPLER: return Rhi::IResource::Type::Sampler; + case D3D_SIT_UAV_RWSTRUCTURED: + case D3D_SIT_CBUFFER: + case D3D_SIT_TBUFFER: return Rhi::IResource::Type::Buffer; + case D3D_SIT_TEXTURE: return Rhi::IResource::Type::Texture; + case D3D_SIT_SAMPLER: return Rhi::IResource::Type::Sampler; + case D3D_SIT_UAV_RWTYPED: return GetResourceTypeByDimensionType(dimension_type); default: META_UNEXPECTED_ARG_DESCR_RETURN(input_type, Rhi::IResource::Type::Buffer, "unable to determine resource type by DX shader input type"); } } +static bool IsUnorderedAccessInputType(D3D_SHADER_INPUT_TYPE input_type) noexcept +{ + return input_type == D3D_SIT_UAV_RWTYPED || + input_type == D3D_SIT_UAV_RWSTRUCTURED || + input_type == D3D_SIT_UAV_RWBYTEADDRESS || + input_type == D3D_SIT_UAV_APPEND_STRUCTURED || + input_type == D3D_SIT_UAV_CONSUME_STRUCTURED || + input_type == D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER || + input_type == D3D_SIT_UAV_FEEDBACKTEXTURE; +} + using StepType = Base::Program::InputBufferLayout::StepType; [[nodiscard]] @@ -153,12 +190,19 @@ Ptrs Shader::GetArgumentBindings(const Rhi::Progra ? Rhi::ProgramArgumentAccessor(shader_argument) : *argument_acc_it; - const ProgramBindings::ArgumentBinding::Type dx_addressable_binding_type = binding_desc.Type == D3D_SIT_CBUFFER - ? ProgramBindings::ArgumentBinding::Type::ConstantBufferView - : ProgramBindings::ArgumentBinding::Type::ShaderResourceView; - const ProgramBindings::ArgumentBinding::Type dx_binding_type = argument_acc.IsAddressable() - ? dx_addressable_binding_type - : ProgramBindings::ArgumentBinding::Type::DescriptorTable; + ProgramBindings::ArgumentBinding::Type dx_binding_type = ProgramBindings::ArgumentBinding::Type::DescriptorTable; + if (argument_acc.IsAddressable()) + { + if (IsUnorderedAccessInputType(binding_desc.Type)) + // SRV or UAV root descriptors can only be Raw or Structured buffers, textures must be bound through DescriptorTable + dx_binding_type = binding_desc.Type == D3D_SIT_UAV_RWTYPED + ? ProgramBindings::ArgumentBinding::Type::DescriptorTable + : ProgramBindings::ArgumentBinding::Type::UnorderedAccessView; + else + dx_binding_type = binding_desc.Type == D3D_SIT_CBUFFER + ? ProgramBindings::ArgumentBinding::Type::ConstantBufferView + : ProgramBindings::ArgumentBinding::Type::ShaderResourceView; + } argument_bindings.push_back(std::make_shared( GetContext(), @@ -167,7 +211,7 @@ Ptrs Shader::GetArgumentBindings(const Rhi::Progra Rhi::IProgramArgumentBinding::Settings { argument_acc, - GetResourceTypeByInputType(binding_desc.Type), + GetResourceTypeByInputAndDimensionType(binding_desc.Type, binding_desc.Dimension), binding_desc.BindCount }, dx_binding_type, diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/System.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/System.cpp index 22d7d2995..2cfc8b70d 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/System.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/System.cpp @@ -79,6 +79,7 @@ static bool EnableDebugLayer() } #ifdef BREAK_ON_DIRECTX_DEBUG_LAYER_MESSAGE_ENABLED + dxgi_info_queue_cptr->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING, true); dxgi_info_queue_cptr->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true); dxgi_info_queue_cptr->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true); #endif @@ -259,7 +260,7 @@ void System::AddDevice(const wrl::ComPtr& cp_adapter, D3D_FEATURE_ return; if (const Rhi::DeviceFeatureMask device_supported_features = Device::GetSupportedFeatures(cp_adapter, feature_level); - !GetDeviceCapabilities().features.HasBits(device_supported_features)) + !device_supported_features.HasBits(GetDeviceCapabilities().features)) return; Base::System::AddDevice(std::make_shared(cp_adapter, feature_level, GetDeviceCapabilities())); diff --git a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Texture.cpp b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Texture.cpp index 3a3b9d930..c9f48dc5c 100644 --- a/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Texture.cpp +++ b/Modules/Graphics/RHI/DirectX/Sources/Methane/Graphics/DirectX/Texture.cpp @@ -77,6 +77,10 @@ static CD3DX12_RESOURCE_DESC CreateNativeResourceDesc(const Rhi::ITexture::Setti META_CHECK_ARG_GREATER_OR_EQUAL(settings.dimensions.GetWidth(), 1); META_CHECK_ARG_GREATER_OR_EQUAL(settings.dimensions.GetHeight(), 1); + D3D12_RESOURCE_FLAGS resource_flags = D3D12_RESOURCE_FLAG_NONE; + if (settings.usage_mask.HasAnyBit(Rhi::ResourceUsage::ShaderWrite)) + resource_flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + CD3DX12_RESOURCE_DESC tex_desc{}; switch (settings.dimension_type) { @@ -91,7 +95,8 @@ static CD3DX12_RESOURCE_DESC CreateNativeResourceDesc(const Rhi::ITexture::Setti TypeConverter::PixelFormatToDxgi(settings.pixel_format), settings.dimensions.GetWidth(), static_cast(sub_resource_count.GetArraySize()), - static_cast(sub_resource_count.GetMipLevelsCount()) + static_cast(sub_resource_count.GetMipLevelsCount()), + resource_flags ); break; @@ -109,7 +114,8 @@ static CD3DX12_RESOURCE_DESC CreateNativeResourceDesc(const Rhi::ITexture::Setti settings.dimensions.GetWidth(), settings.dimensions.GetHeight(), static_cast(sub_resource_count.GetArraySize()), - static_cast(sub_resource_count.GetMipLevelsCount()) + static_cast(sub_resource_count.GetMipLevelsCount()), + 1, 0, resource_flags ); break; @@ -120,7 +126,8 @@ static CD3DX12_RESOURCE_DESC CreateNativeResourceDesc(const Rhi::ITexture::Setti settings.dimensions.GetWidth(), settings.dimensions.GetHeight(), static_cast(sub_resource_count.GetDepth()), - static_cast(sub_resource_count.GetMipLevelsCount()) + static_cast(sub_resource_count.GetMipLevelsCount()), + resource_flags ); break; @@ -135,7 +142,8 @@ static CD3DX12_RESOURCE_DESC CreateNativeResourceDesc(const Rhi::ITexture::Setti settings.dimensions.GetWidth(), settings.dimensions.GetHeight(), static_cast(sub_resource_count.GetDepth() * sub_resource_count.GetArraySize()), - static_cast(sub_resource_count.GetMipLevelsCount()) + static_cast(sub_resource_count.GetMipLevelsCount()), + 1, 0, resource_flags ); break; @@ -214,6 +222,57 @@ static D3D12_SHADER_RESOURCE_VIEW_DESC CreateNativeShaderResourceViewDesc(const return srv_desc; } +[[nodiscard]] +static D3D12_UNORDERED_ACCESS_VIEW_DESC CreateNativeUnorderedAccessViewDesc(const Rhi::ITexture::Settings& settings, const ResourceView::Id& view_id) +{ + META_FUNCTION_TASK(); + const Rhi::IResource::SubResource::Index& sub_resource_index = view_id.subresource_index; + const Rhi::IResource::SubResource::Count& sub_resource_count = view_id.subresource_count; + + D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc{}; + switch (settings.dimension_type) + { + case Rhi::TextureDimensionType::Tex1D: + uav_desc.Texture1D.MipSlice = sub_resource_index.GetMipLevel(); + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D; + break; + + case Rhi::TextureDimensionType::Tex1DArray: + uav_desc.Texture1DArray.MipSlice = sub_resource_index.GetMipLevel(); + uav_desc.Texture1DArray.FirstArraySlice = sub_resource_index.GetArrayIndex(); + uav_desc.Texture1DArray.ArraySize = sub_resource_count.GetArraySize(); + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1DARRAY; + break; + + case Rhi::TextureDimensionType::Tex2D: + uav_desc.Texture2D.PlaneSlice = 0U; + uav_desc.Texture2D.MipSlice = sub_resource_index.GetMipLevel(); + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + break; + + case Rhi::TextureDimensionType::Tex2DArray: + uav_desc.Texture2DArray.PlaneSlice = 0U; + uav_desc.Texture2DArray.MipSlice = sub_resource_index.GetMipLevel(); + uav_desc.Texture2DArray.FirstArraySlice = sub_resource_index.GetArrayIndex(); + uav_desc.Texture2DArray.ArraySize = sub_resource_count.GetArraySize(); + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + break; + + case Rhi::TextureDimensionType::Tex3D: + uav_desc.Texture3D.FirstWSlice = sub_resource_index.GetDepthSlice(); + uav_desc.Texture3D.WSize = sub_resource_count.GetDepth(); + uav_desc.Texture3D.MipSlice = sub_resource_index.GetArrayIndex(); + uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D; + break; + + default: + META_UNEXPECTED_ARG(settings.dimension_type); + } + + uav_desc.Format = TypeConverter::PixelFormatToDxgi(settings.pixel_format); + return uav_desc; +} + [[nodiscard]] static D3D12_RENDER_TARGET_VIEW_DESC CreateNativeRenderTargetViewDesc(const Rhi::ITexture::Settings& settings, const ResourceView::Id& view_id) @@ -291,15 +350,20 @@ bool Texture::SetName(std::string_view name) { m_cp_upload_resource->SetName(nowide::widen(fmt::format("{} Upload Resource", name)).c_str()); } + if (m_cp_read_back_resource) + { + m_cp_read_back_resource->SetName(nowide::widen(fmt::format("{} Read-back Resource", name)).c_str()); + } + return true; } -void Texture::SetData(const SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) +void Texture::SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResources& sub_resources) { META_FUNCTION_TASK(); META_CHECK_ARG_NOT_NULL_DESCR(m_cp_upload_resource, "Only ForImage textures support data upload from CPU."); - Resource::SetData(sub_resources, target_cmd_queue); + Base::Texture::SetData(target_cmd_queue, sub_resources); const Settings& settings = GetSettings(); const Data::Size pixel_size = GetPixelSize(settings.pixel_format); @@ -331,13 +395,67 @@ void Texture::SetData(const SubResources& sub_resources, Rhi::ICommandQueue& tar } // Upload texture subresources data to GPU via intermediate upload resource - const TransferCommandList& upload_cmd_list = PrepareResourceUpload(target_cmd_queue); + const TransferCommandList& upload_cmd_list = PrepareResourceTransfer(TransferOperation::Upload, target_cmd_queue, State::CopyDest); UpdateSubresources(&upload_cmd_list.GetNativeCommandList(), GetNativeResource(), m_cp_upload_resource.Get(), 0, 0, static_cast(dx_sub_resources.size()), dx_sub_resources.data()); GetContext().RequestDeferredAction(Rhi::IContext::DeferredAction::UploadResources); } +Rhi::SubResource Texture::GetData(Rhi::ICommandQueue& target_cmd_queue, const SubResource::Index& sub_resource_index, const BytesRangeOpt& data_range) +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_TRUE_DESCR(GetUsage().HasAnyBit(Rhi::ResourceUsage::ReadBack), + "getting texture data from GPU is allowed for buffers with CPU Read-back flag only"); + META_CHECK_ARG_NOT_NULL(m_cp_read_back_resource); + + ValidateSubResource(sub_resource_index, data_range); + + const TransferCommandList& transfer_cmd_list = PrepareResourceTransfer(TransferOperation::Readback, target_cmd_queue, State::CopySource); + + const Settings& settings = GetSettings(); + const Data::Index sub_resource_raw_index = sub_resource_index.GetRawIndex(GetSubresourceCount()); + const DXGI_FORMAT pixel_format = TypeConverter::PixelFormatToDxgi(settings.pixel_format, TypeConverter::ResourceFormatType::ViewRead); + + const D3D12_PLACED_SUBRESOURCE_FOOTPRINT src_footprint { 0U, + D3D12_SUBRESOURCE_FOOTPRINT { + pixel_format, + settings.dimensions.GetWidth(), + settings.dimensions.GetHeight(), + settings.dimensions.GetDepth(), + static_cast(settings.dimensions.GetWidth() * ::DirectX::BitsPerPixel(pixel_format) / 8) + } + }; + const CD3DX12_TEXTURE_COPY_LOCATION src_copy_location(GetNativeResource(), sub_resource_raw_index); + const CD3DX12_TEXTURE_COPY_LOCATION dst_copy_location(m_cp_read_back_resource.Get(), src_footprint); + transfer_cmd_list.GetNativeCommandList().CopyTextureRegion(&dst_copy_location, 0, 0, 0, &src_copy_location, nullptr); + + GetBaseContext().UploadResources(); + + const Data::Index data_start = data_range ? data_range->GetStart() : 0U; + const Data::Index data_length = data_range ? data_range->GetLength() : GetSubResourceDataSize(sub_resource_index); + const Data::Index data_end = data_start + data_length; + + const CD3DX12_RANGE read_range(data_start, data_start + data_length); + Data::RawPtr p_sub_resource_data = nullptr; + ThrowIfFailed( + m_cp_read_back_resource->Map(sub_resource_raw_index, &read_range, + reinterpret_cast(&p_sub_resource_data)), // NOSONAR + GetDirectContext().GetDirectDevice().GetNativeDevice().Get() + ); + + META_CHECK_ARG_NOT_NULL_DESCR(p_sub_resource_data, "failed to map buffer subresource"); + + stdext::checked_array_iterator source_data_it(p_sub_resource_data, data_end); + Data::Bytes sub_resource_data(data_length, {}); + std::copy(source_data_it + data_start, source_data_it + data_end, sub_resource_data.begin()); + + const CD3DX12_RANGE zero_write_range(0, 0); + m_cp_read_back_resource->Unmap(sub_resource_raw_index, &zero_write_range); + + return SubResource(std::move(sub_resource_data), sub_resource_index, data_range); +} + Opt Texture::InitializeNativeViewDescriptor(const View::Id& view_id) { META_FUNCTION_TASK(); @@ -347,7 +465,15 @@ Opt Texture::InitializeNativeViewDescriptor(const Vi switch(settings.type) { case Rhi::TextureType::Image: - CreateShaderResourceView(descriptor, view_id); + if (view_id.usage.HasAnyBit(Usage::ShaderWrite)) + CreateUnorderedAccessView(descriptor, view_id); + else if (view_id.usage.HasAnyBit(Usage::ShaderRead)) + CreateShaderResourceView(descriptor, view_id); + else + { + META_UNEXPECTED_ARG_DESCR_RETURN(view_id.usage.GetValue(), descriptor, + "unsupported usage {} for Image texture", Data::GetEnumMaskName(view_id.usage)); + } break; case Rhi::TextureType::FrameBuffer: @@ -396,8 +522,13 @@ void Texture::InitializeAsImage() const CD3DX12_RESOURCE_DESC resource_desc = CreateNativeResourceDesc(settings, sub_resource_count); InitializeCommittedResource(resource_desc, D3D12_HEAP_TYPE_DEFAULT, Rhi::ResourceState::CopyDest); - const UINT64 upload_buffer_size = GetRequiredIntermediateSize(GetNativeResource(), 0, GetSubresourceCount().GetRawCount()); - m_cp_upload_resource = CreateCommittedResource(CD3DX12_RESOURCE_DESC::Buffer(upload_buffer_size), D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ); + const UINT64 texture_buffer_size = GetRequiredIntermediateSize(GetNativeResource(), 0, GetSubresourceCount().GetRawCount()); + m_cp_upload_resource = CreateCommittedResource(CD3DX12_RESOURCE_DESC::Buffer(texture_buffer_size), D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ); + + if (settings.usage_mask.HasAnyBit(Usage::ReadBack)) + { + m_cp_read_back_resource = CreateCommittedResource(CD3DX12_RESOURCE_DESC::Buffer(texture_buffer_size), D3D12_HEAP_TYPE_READBACK, D3D12_RESOURCE_STATE_COPY_DEST); + } } void Texture::InitializeAsRenderTarget() @@ -417,7 +548,13 @@ void Texture::InitializeAsFrameBuffer() META_CHECK_ARG_EQUAL(settings.type, Rhi::TextureType::FrameBuffer); META_CHECK_ARG_TRUE_DESCR(GetUsage().HasAnyBit(Usage::RenderTarget), "frame-buffer texture supports only 'RenderTarget' usage"); META_CHECK_ARG_TRUE_DESCR(GetSettings().frame_index_opt.has_value(), "frame-buffer texture requires frame-index to be set in texture settings"); - InitializeFrameBufferResource(settings.frame_index_opt.value()); + + wrl::ComPtr cp_resource; + ThrowIfFailed( + static_cast(GetDirectContext()).GetNativeSwapChain()->GetBuffer(settings.frame_index_opt.value(), IID_PPV_ARGS(&cp_resource)), + GetDirectContext().GetDirectDevice().GetNativeDevice().Get() + ); + SetNativeResourceComPtr(cp_resource); } void Texture::InitializeAsDepthStencil() @@ -476,6 +613,13 @@ void Texture::CreateShaderResourceView(const Descriptor& descriptor, const View: GetDirectContext().GetDirectDevice().GetNativeDevice()->CreateShaderResourceView(GetNativeResource(), &srv_desc, GetNativeCpuDescriptorHandle(descriptor)); } +void Texture::CreateUnorderedAccessView(const Descriptor& descriptor, const View::Id& view_id) const +{ + META_FUNCTION_TASK(); + const D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc = CreateNativeUnorderedAccessViewDesc(GetSettings(), view_id); + GetDirectContext().GetDirectDevice().GetNativeDevice()->CreateUnorderedAccessView(GetNativeResource(), nullptr, &uav_desc, GetNativeCpuDescriptorHandle(descriptor)); +} + void Texture::CreateRenderTargetView(const Descriptor& descriptor) const { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Impl/CMakeLists.txt b/Modules/Graphics/RHI/Impl/CMakeLists.txt index b1c705421..9f862b46c 100644 --- a/Modules/Graphics/RHI/Impl/CMakeLists.txt +++ b/Modules/Graphics/RHI/Impl/CMakeLists.txt @@ -19,8 +19,10 @@ list(APPEND HEADERS ${INCLUDE_DIR}/RenderPattern.h ${INCLUDE_DIR}/RenderPass.h ${INCLUDE_DIR}/RenderContext.h + ${INCLUDE_DIR}/ComputeContext.h ${INCLUDE_DIR}/RenderState.h ${INCLUDE_DIR}/ViewState.h + ${INCLUDE_DIR}/ComputeState.h ${INCLUDE_DIR}/Buffer.h ${INCLUDE_DIR}/BufferSet.h ${INCLUDE_DIR}/Texture.h @@ -29,6 +31,7 @@ list(APPEND HEADERS ${INCLUDE_DIR}/RenderCommandList.h ${INCLUDE_DIR}/ParallelRenderCommandList.h ${INCLUDE_DIR}/TransferCommandList.h + ${INCLUDE_DIR}/ComputeCommandList.h ) list(APPEND SOURCES @@ -45,8 +48,10 @@ list(APPEND SOURCES ${SOURCES_DIR}/RenderPattern.cpp ${SOURCES_DIR}/RenderPass.cpp ${SOURCES_DIR}/RenderContext.cpp + ${SOURCES_DIR}/ComputeContext.cpp ${SOURCES_DIR}/RenderState.cpp ${SOURCES_DIR}/ViewState.cpp + ${SOURCES_DIR}/ComputeState.cpp ${SOURCES_DIR}/Buffer.cpp ${SOURCES_DIR}/BufferSet.cpp ${SOURCES_DIR}/Texture.cpp @@ -55,6 +60,7 @@ list(APPEND SOURCES ${SOURCES_DIR}/RenderCommandList.cpp ${SOURCES_DIR}/ParallelRenderCommandList.cpp ${SOURCES_DIR}/TransferCommandList.cpp + ${SOURCES_DIR}/ComputeCommandList.cpp ) if (METHANE_GFX_API EQUAL METHANE_GFX_DIRECTX) diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Buffer.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Buffer.h index 38f349b75..f073be1b3 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Buffer.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Buffer.h @@ -39,6 +39,8 @@ namespace Methane::Graphics::Rhi class ResourceBarriers; class CommandQueue; +class RenderContext; +class ComputeContext; class Buffer // NOSONAR - class has more than 35 methods, constructors and assignment operators are required to use forward declared Impl and Ptr in header { @@ -62,6 +64,8 @@ class Buffer // NOSONAR - class has more than 35 methods, constructors and assig META_PIMPL_API explicit Buffer(const Ptr& interface_ptr); META_PIMPL_API explicit Buffer(IBuffer& interface_ref); META_PIMPL_API Buffer(const IContext& context, const BufferSettings& settings); + META_PIMPL_API Buffer(const RenderContext& context, const BufferSettings& settings); + META_PIMPL_API Buffer(const ComputeContext& context, const BufferSettings& settings); META_PIMPL_API bool IsInitialized() const META_PIMPL_NOEXCEPT; META_PIMPL_API IBuffer& GetInterface() const META_PIMPL_NOEXCEPT; @@ -80,13 +84,9 @@ class Buffer // NOSONAR - class has more than 35 methods, constructors and assig META_PIMPL_API bool SetState(State state, Barriers& out_barriers) const; META_PIMPL_API bool SetOwnerQueueFamily(uint32_t family_index) const; META_PIMPL_API bool SetOwnerQueueFamily(uint32_t family_index, Barriers& out_barriers) const; - META_PIMPL_API void SetData(const SubResources& sub_resources, const CommandQueue& target_cmd_queue) const; META_PIMPL_API void RestoreDescriptorViews(const DescriptorByViewId& descriptor_by_view_id) const; - [[nodiscard]] META_PIMPL_API SubResource GetData(const SubResource::Index& sub_resource_index = SubResource::Index(), const BytesRangeOpt& data_range = {}) const; [[nodiscard]] META_PIMPL_API Data::Size GetDataSize(Data::MemoryState size_type = Data::MemoryState::Reserved) const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API Data::Size GetSubResourceDataSize(const SubResource::Index& sub_resource_index = SubResource::Index()) const; - [[nodiscard]] META_PIMPL_API const SubResource::Count& GetSubresourceCount() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API ResourceType GetResourceType() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API State GetState() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API ResourceUsageMask GetUsage() const META_PIMPL_NOEXCEPT; @@ -101,6 +101,8 @@ class Buffer // NOSONAR - class has more than 35 methods, constructors and assig // IBuffer interface methods [[nodiscard]] META_PIMPL_API const Settings& GetSettings() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API uint32_t GetFormattedItemsCount() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API SubResource GetData(const Rhi::CommandQueue& target_cmd_queue, const BytesRangeOpt& data_range = {}) const; + META_PIMPL_API void SetData(const CommandQueue& target_cmd_queue, const SubResource& sub_resource) const; private: using Impl = Methane::Graphics::META_GFX_NAME::Buffer; diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/CommandKit.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/CommandKit.h index 4ea7cf0d0..34ca9c33c 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/CommandKit.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/CommandKit.h @@ -38,6 +38,8 @@ namespace Methane::Graphics::Rhi class CommandQueue; class RenderContext; class RenderCommandList; +class ComputeCommandList; +class TransferCommandList; class CommandListSet; class CommandKit // NOSONAR - constructors and assignment operators are required to use forward declared Impl and Ptr in header @@ -64,15 +66,21 @@ class CommandKit // NOSONAR - constructors and assignment operators are required META_PIMPL_API void Disconnect(Data::Receiver& receiver) const; // ICommandKit interface methods - [[nodiscard]] META_PIMPL_API const IContext& GetContext() const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API CommandQueue GetQueue() const; - [[nodiscard]] META_PIMPL_API CommandListType GetListType() const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API bool HasList(CommandListId cmd_list_id = 0U) const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API bool HasListWithState(CommandListState cmd_list_state, CommandListId cmd_list_id = 0U) const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API RenderCommandList GetRenderList(CommandListId cmd_list_id = 0U) const; - [[nodiscard]] META_PIMPL_API RenderCommandList GetRenderListForEncoding(CommandListId cmd_list_id = 0U, std::string_view debug_group_name = {}) const; - [[nodiscard]] META_PIMPL_API CommandListSet GetListSet(const std::vector& cmd_list_ids = { 0U }, Opt frame_index_opt = {}) const; - [[nodiscard]] META_PIMPL_API IFence& GetFence(CommandListId fence_id = 0U) const; + [[nodiscard]] META_PIMPL_API const IContext& GetContext() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API CommandQueue GetQueue() const; + [[nodiscard]] META_PIMPL_API CommandListType GetListType() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API bool HasList(CommandListId cmd_list_id = 0U) const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API bool HasListWithState(CommandListState cmd_list_state, CommandListId cmd_list_id = 0U) const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API RenderCommandList GetRenderList(CommandListId cmd_list_id = 0U) const; + [[nodiscard]] META_PIMPL_API RenderCommandList GetRenderListForEncoding(CommandListId cmd_list_id = 0U, std::string_view debug_group_name = {}) const; + [[nodiscard]] META_PIMPL_API ComputeCommandList GetComputeList(CommandListId cmd_list_id = 0U) const; + [[nodiscard]] META_PIMPL_API ComputeCommandList GetComputeListForEncoding(CommandListId cmd_list_id = 0U, std::string_view debug_group_name = {}) const; + [[nodiscard]] META_PIMPL_API TransferCommandList GetTransferList(CommandListId cmd_list_id = 0U) const; + [[nodiscard]] META_PIMPL_API TransferCommandList GetTransferListForEncoding(CommandListId cmd_list_id = 0U, std::string_view debug_group_name = {}) const; + [[nodiscard]] META_PIMPL_API CommandListSet GetListSet(const std::vector& cmd_list_ids = { 0U }, Opt frame_index_opt = {}) const; + [[nodiscard]] META_PIMPL_API IFence& GetFence(CommandListId fence_id = 0U) const; + META_PIMPL_API void ExecuteListSet(const std::vector& cmd_list_ids = { 0U }, Opt frame_index_opt = {}) const; + META_PIMPL_API void ExecuteListSetAndWaitForCompletion(const std::vector& cmd_list_ids = { 0U }, Opt frame_index_opt = {}) const; private: using Impl = Methane::Graphics::Base::CommandKit; diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/CommandQueue.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/CommandQueue.h index fce439384..9cb802eae 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/CommandQueue.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/CommandQueue.h @@ -40,9 +40,11 @@ class CommandListSet; class Fence; class RenderPass; class RenderContext; +class ComputeContext; class RenderCommandList; class ParallelRenderCommandList; class TransferCommandList; +class ComputeCommandList; class CommandQueue // NOSONAR - constructors and assignment operators are required to use forward declared Impl and Ptr in header { @@ -53,6 +55,7 @@ class CommandQueue // NOSONAR - constructors and assignment operators are requir META_PIMPL_API explicit CommandQueue(const Ptr& interface_ptr); META_PIMPL_API explicit CommandQueue(ICommandQueue& interface_ref); META_PIMPL_API CommandQueue(const RenderContext& context, CommandListType command_lists_type); + META_PIMPL_API CommandQueue(const ComputeContext& context, CommandListType command_lists_type); META_PIMPL_API bool IsInitialized() const META_PIMPL_NOEXCEPT; META_PIMPL_API ICommandQueue& GetInterface() const META_PIMPL_NOEXCEPT; @@ -70,6 +73,7 @@ class CommandQueue // NOSONAR - constructors and assignment operators are requir [[nodiscard]] META_PIMPL_API CommandKit CreateCommandKit() const; [[nodiscard]] META_PIMPL_API Fence CreateFence() const; [[nodiscard]] META_PIMPL_API TransferCommandList CreateTransferCommandList() const; + [[nodiscard]] META_PIMPL_API ComputeCommandList CreateComputeCommandList() const; [[nodiscard]] META_PIMPL_API RenderCommandList CreateRenderCommandList(const RenderPass& render_pass) const; [[nodiscard]] META_PIMPL_API ParallelRenderCommandList CreateParallelRenderCommandList(const RenderPass& render_pass) const; [[nodiscard]] META_PIMPL_API const IContext& GetContext() const META_PIMPL_NOEXCEPT; diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeCommandList.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeCommandList.h new file mode 100644 index 000000000..72f05ae30 --- /dev/null +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeCommandList.h @@ -0,0 +1,106 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/ComputeCommandList.h +Methane ComputeCommandList PIMPL wrappers for direct calls to final implementation. + +******************************************************************************/ + +#pragma once + +#include + +#include + +namespace Methane::Graphics::META_GFX_NAME +{ +class ComputeCommandList; +} + +namespace Methane::Graphics::Rhi +{ + +class CommandQueue; +class CommandListDebugGroup; +class ComputeState; +class ProgramBindings; + +class ComputeCommandList // NOSONAR - constructors and assignment operators are required to use forward declared Impl and Ptr in header +{ +public: + using Type = CommandListType; + using State = CommandListState; + using DebugGroup = CommandListDebugGroup; + using ICallback = ICommandListCallback; + + META_PIMPL_DEFAULT_CONSTRUCT_METHODS_DECLARE(ComputeCommandList); + META_PIMPL_METHODS_COMPARE_DECLARE(ComputeCommandList); + + META_PIMPL_API explicit ComputeCommandList(const Ptr& interface_ptr); + META_PIMPL_API explicit ComputeCommandList(IComputeCommandList& interface_ref); + META_PIMPL_API explicit ComputeCommandList(const CommandQueue& command_queue); + + META_PIMPL_API bool IsInitialized() const META_PIMPL_NOEXCEPT; + META_PIMPL_API IComputeCommandList& GetInterface() const META_PIMPL_NOEXCEPT; + META_PIMPL_API Ptr GetInterfacePtr() const META_PIMPL_NOEXCEPT; + + // IObject interface methods + META_PIMPL_API bool SetName(std::string_view name) const; + META_PIMPL_API std::string_view GetName() const META_PIMPL_NOEXCEPT; + + // Data::IEmitter interface methods + META_PIMPL_API void Connect(Data::Receiver& receiver) const; + META_PIMPL_API void Disconnect(Data::Receiver& receiver) const; + + // ICommandList interface methods + META_PIMPL_API void PushDebugGroup(const DebugGroup& debug_group) const; + META_PIMPL_API void PopDebugGroup() const; + META_PIMPL_API void Reset(const DebugGroup* debug_group_ptr = nullptr) const; + META_PIMPL_API void ResetOnce(const DebugGroup* debug_group_ptr = nullptr) const; + META_PIMPL_API void SetProgramBindings(const ProgramBindings& program_bindings, + ProgramBindingsApplyBehaviorMask apply_behavior = ProgramBindingsApplyBehaviorMask(~0U)) const; + META_PIMPL_API void SetResourceBarriers(const IResourceBarriers& resource_barriers) const; + META_PIMPL_API void Commit() const; + META_PIMPL_API void WaitUntilCompleted(uint32_t timeout_ms = 0U) const; + [[nodiscard]] META_PIMPL_API Data::TimeRange GetGpuTimeRange(bool in_cpu_nanoseconds) const; + [[nodiscard]] META_PIMPL_API State GetState() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API CommandQueue GetCommandQueue() const; + + // Data::IEmitter interface methods + META_PIMPL_API void Connect(Data::Receiver& receiver) const; + META_PIMPL_API void Disconnect(Data::Receiver& receiver) const; + + // IComputeCommandList interface + META_PIMPL_API void ResetWithState(const ComputeState& compute_state, const DebugGroup* debug_group_ptr = nullptr) const; + META_PIMPL_API void ResetWithStateOnce(const ComputeState& compute_state, const DebugGroup* debug_group_ptr = nullptr) const; + META_PIMPL_API void SetComputeState(const ComputeState& compute_state) const; + META_PIMPL_API void Dispatch(const ThreadGroupsCount& thread_groups_count) const; + +private: + using Impl = Methane::Graphics::META_GFX_NAME::ComputeCommandList; + + Ptr m_impl_ptr; +}; + +} // namespace Methane::Graphics::Rhi + +#ifdef META_PIMPL_INLINE + +#include + +#endif // META_PIMPL_INLINE diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeContext.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeContext.h new file mode 100644 index 000000000..a411c81a7 --- /dev/null +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeContext.h @@ -0,0 +1,132 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/ComputeContext.h +Methane ComputeContext PIMPL wrappers for direct calls to final implementation. + +******************************************************************************/ + +#pragma once + +#include + +#include + +namespace Methane::Graphics::META_GFX_NAME +{ +class ComputeContext; +} + +namespace Methane::Graphics::Rhi +{ + +class Device; +class CommandKit; +class CommandQueue; +class Shader; +class Program; +class ComputeState; +class Buffer; +class Texture; +class Sampler; + +struct ShaderSettings; +struct ProgramSettingsImpl; +struct ComputeStateSettingsImpl; +struct BufferSettings; +struct TextureSettings; +struct SamplerSettings; + +enum class CommandListType; +enum class ShaderType : uint32_t; + +class ComputeContext // NOSONAR - constructors and assignment operators are required to use forward declared Impl and Ptr in header +{ +public: + using Settings = ComputeContextSettings; + using Type = ContextType; + using WaitFor = ContextWaitFor; + using DeferredAction = ContextDeferredAction; + using Option = ContextOption; + using OptionMask = ContextOptionMask; + using IncompatibleException = ContextIncompatibleException; + + META_PIMPL_DEFAULT_CONSTRUCT_METHODS_DECLARE(ComputeContext); + META_PIMPL_METHODS_COMPARE_DECLARE(ComputeContext); + + META_PIMPL_API explicit ComputeContext(const Ptr& render_context_ptr); + META_PIMPL_API explicit ComputeContext(IComputeContext& render_context); + META_PIMPL_API ComputeContext(const Device& device, tf::Executor& parallel_executor, const Settings& settings); + + META_PIMPL_API bool IsInitialized() const META_PIMPL_NOEXCEPT; + META_PIMPL_API IComputeContext& GetInterface() const META_PIMPL_NOEXCEPT; + META_PIMPL_API Ptr GetInterfacePtr() const META_PIMPL_NOEXCEPT; + + // IObject interface methods + META_PIMPL_API bool SetName(std::string_view name) const; + META_PIMPL_API std::string_view GetName() const META_PIMPL_NOEXCEPT; + + // Data::IEmitter interface methods + META_PIMPL_API void Connect(Data::Receiver& receiver) const; + META_PIMPL_API void Disconnect(Data::Receiver& receiver) const; + + // IContext interface methods + [[nodiscard]] META_PIMPL_API CommandQueue CreateCommandQueue(CommandListType type) const; + [[nodiscard]] META_PIMPL_API CommandKit CreateCommandKit(CommandListType type) const; + [[nodiscard]] META_PIMPL_API Shader CreateShader(ShaderType type, const ShaderSettings& settings) const; + [[nodiscard]] META_PIMPL_API Program CreateProgram(const ProgramSettingsImpl& settings) const; + [[nodiscard]] META_PIMPL_API ComputeState CreateComputeState(const ComputeStateSettingsImpl& settings) const; + [[nodiscard]] META_PIMPL_API Buffer CreateBuffer(const BufferSettings& settings) const; + [[nodiscard]] META_PIMPL_API Texture CreateTexture(const TextureSettings& settings) const; + [[nodiscard]] META_PIMPL_API Sampler CreateSampler(const SamplerSettings& settings) const; + [[nodiscard]] META_PIMPL_API OptionMask GetOptions() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API tf::Executor& GetParallelExecutor() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API IObjectRegistry& GetObjectRegistry() const META_PIMPL_NOEXCEPT; + META_PIMPL_API bool UploadResources() const META_PIMPL_NOEXCEPT; + META_PIMPL_API void RequestDeferredAction(DeferredAction action) const META_PIMPL_NOEXCEPT; + META_PIMPL_API void CompleteInitialization() const; + [[nodiscard]] META_PIMPL_API bool IsCompletingInitialization() const META_PIMPL_NOEXCEPT; + META_PIMPL_API void WaitForGpu(WaitFor wait_for) const; + META_PIMPL_API void Reset(const Device& device) const; + META_PIMPL_API void Reset() const; + [[nodiscard]] META_PIMPL_API Device GetDevice() const; + [[nodiscard]] META_PIMPL_API CommandKit GetDefaultCommandKit(CommandListType type) const; + [[nodiscard]] META_PIMPL_API CommandKit GetDefaultCommandKit(const CommandQueue& cmd_queue) const; + [[nodiscard]] META_PIMPL_API CommandKit GetUploadCommandKit() const; + [[nodiscard]] META_PIMPL_API CommandKit GetComputeCommandKit() const; + + // Data::IEmitter interface methods + META_PIMPL_API void Connect(Data::Receiver& receiver) const; + META_PIMPL_API void Disconnect(Data::Receiver& receiver) const; + + // IComputeContext interface methods + [[nodiscard]] META_PIMPL_API const Settings& GetSettings() const META_PIMPL_NOEXCEPT; + +private: + using Impl = Methane::Graphics::META_GFX_NAME::ComputeContext; + + Ptr m_impl_ptr; +}; + +} // namespace Methane::Graphics::Rhi + +#ifdef META_PIMPL_INLINE + +#include + +#endif // META_PIMPL_INLINE diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeState.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeState.h new file mode 100644 index 000000000..76d918a6b --- /dev/null +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/ComputeState.h @@ -0,0 +1,94 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/ComputeState.h +Methane ComputeState PIMPL wrappers for direct calls to final implementation. + +******************************************************************************/ + +#pragma once + +#include +#include "Program.h" + +#include + +namespace Methane::Graphics::META_GFX_NAME +{ +class ComputeState; +} + +namespace Methane::Graphics::Rhi +{ + +struct ComputeStateSettingsImpl +{ + Program program; + ThreadGroupSize thread_group_size; // Duplicated in HLSL attribute [numthreads(x,y,z)] of compute shader, but Metal does not use it + + META_PIMPL_API static ComputeStateSettings Convert(const ComputeStateSettingsImpl& settings); +}; + +class RenderContext; +class ComputeContext; + +class ComputeState // NOSONAR - constructors and assignment operators are required to use forward declared Impl and Ptr in header +{ +public: + using Settings = ComputeStateSettingsImpl; + + META_PIMPL_DEFAULT_CONSTRUCT_METHODS_DECLARE(ComputeState); + META_PIMPL_METHODS_COMPARE_DECLARE(ComputeState); + + META_PIMPL_API explicit ComputeState(const Ptr& interface_ptr); + META_PIMPL_API explicit ComputeState(IComputeState& interface_ref); + META_PIMPL_API ComputeState(const RenderContext& context, const Settings& settings); + META_PIMPL_API ComputeState(const ComputeContext& context, const Settings& settings); + + META_PIMPL_API bool IsInitialized() const META_PIMPL_NOEXCEPT; + META_PIMPL_API IComputeState& GetInterface() const META_PIMPL_NOEXCEPT; + META_PIMPL_API Ptr GetInterfacePtr() const META_PIMPL_NOEXCEPT; + + // IObject interface methods + META_PIMPL_API bool SetName(std::string_view name) const; + META_PIMPL_API std::string_view GetName() const META_PIMPL_NOEXCEPT; + + // Data::IEmitter interface methods + META_PIMPL_API void Connect(Data::Receiver& receiver) const; + META_PIMPL_API void Disconnect(Data::Receiver& receiver) const; + + // IComputeState interface methods + [[nodiscard]] META_PIMPL_API const ComputeStateSettings& GetSettings() const META_PIMPL_NOEXCEPT; + META_PIMPL_API void Reset(const Settings& settings) const; + META_PIMPL_API void Reset(const IComputeState::Settings& settings) const; + + META_PIMPL_API Program GetProgram() const; + +private: + using Impl = Methane::Graphics::META_GFX_NAME::ComputeState; + + Ptr m_impl_ptr; +}; + +} // namespace Methane::Graphics::Rhi + +#ifdef META_PIMPL_INLINE + +#include + +#endif // META_PIMPL_INLINE diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Device.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Device.h index ff7ab7b8a..4e6a6d289 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Device.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Device.h @@ -36,6 +36,7 @@ namespace Methane::Graphics::Rhi { class RenderContext; +class ComputeContext; class Device // NOSONAR - constructors and assignment operators are required to use forward declared Impl and Ptr in header { @@ -62,6 +63,7 @@ class Device // NOSONAR - constructors and assignment operators are required to // IDevice interface methods [[nodiscard]] META_PIMPL_API RenderContext CreateRenderContext(const Platform::AppEnvironment& env, tf::Executor& parallel_executor, const RenderContextSettings& settings) const; + [[nodiscard]] META_PIMPL_API ComputeContext CreateComputeContext(tf::Executor& parallel_executor, const ComputeContextSettings& settings) const; [[nodiscard]] META_PIMPL_API const std::string& GetAdapterName() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API bool IsSoftwareAdapter() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API const Capabilities& GetCapabilities() const META_PIMPL_NOEXCEPT; diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Fence.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Fence.h index 9893930ec..ad17f6eed 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Fence.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Fence.h @@ -62,9 +62,9 @@ class Fence // NOSONAR - constructors and assignment operators are required to u // IFence interface methods META_PIMPL_API void Signal() const; META_PIMPL_API void WaitOnCpu() const; - META_PIMPL_API void WaitOnGpu(ICommandQueue& wait_on_command_queue) const; + META_PIMPL_API void WaitOnGpu(const CommandQueue& wait_on_command_queue) const; META_PIMPL_API void FlushOnCpu() const; - META_PIMPL_API void FlushOnGpu(ICommandQueue& wait_on_command_queue) const; + META_PIMPL_API void FlushOnGpu(const CommandQueue& wait_on_command_queue) const; private: using Impl = Methane::Graphics::META_GFX_NAME::Fence; diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Implementations.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Implementations.h index dd6ea23b5..56008abcc 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Implementations.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Implementations.h @@ -37,6 +37,8 @@ Methane graphics RHI PIMPL implementations. #include "RenderPass.h" #include "RenderContext.h" #include "RenderState.h" +#include "ComputeContext.h" +#include "ComputeState.h" #include "ViewState.h" #include "Buffer.h" #include "BufferSet.h" @@ -46,3 +48,4 @@ Methane graphics RHI PIMPL implementations. #include "RenderCommandList.h" #include "ParallelRenderCommandList.h" #include "TransferCommandList.h" +#include "ComputeCommandList.h" diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Program.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Program.h index 1415c29a7..5a89cdd40 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Program.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Program.h @@ -48,6 +48,7 @@ struct ProgramSettingsImpl }; class RenderContext; +class ComputeContext; class Shader; class ProgramBindings; @@ -70,6 +71,7 @@ class Program // NOSONAR - constructors and assignment operators are required to META_PIMPL_API explicit Program(const Ptr& interface_ptr); META_PIMPL_API explicit Program(IProgram& interface_ref); META_PIMPL_API Program(const RenderContext& context, const Settings& settings); + META_PIMPL_API Program(const ComputeContext& context, const Settings& settings); META_PIMPL_API bool IsInitialized() const META_PIMPL_NOEXCEPT; META_PIMPL_API IProgram& GetInterface() const META_PIMPL_NOEXCEPT; diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/RenderContext.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/RenderContext.h index 0d6883068..e0f495306 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/RenderContext.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/RenderContext.h @@ -45,6 +45,7 @@ class Texture; class Sampler; class RenderState; class RenderPattern; +class ComputeState; struct ShaderSettings; struct ProgramSettingsImpl; @@ -53,6 +54,7 @@ struct TextureSettings; struct SamplerSettings; struct RenderStateSettingsImpl; struct RenderPatternSettings; +struct ComputeStateSettingsImpl; enum class CommandListType; enum class ShaderType : uint32_t; @@ -96,10 +98,12 @@ class RenderContext // NOSONAR - class has more than 35 methods, constructors an [[nodiscard]] META_PIMPL_API Texture CreateTexture(const TextureSettings& settings) const; [[nodiscard]] META_PIMPL_API Sampler CreateSampler(const SamplerSettings& settings) const; [[nodiscard]] META_PIMPL_API RenderState CreateRenderState(const RenderStateSettingsImpl& settings) const; + [[nodiscard]] META_PIMPL_API ComputeState CreateComputeState(const ComputeStateSettingsImpl& settings) const; [[nodiscard]] META_PIMPL_API RenderPattern CreateRenderPattern(const RenderPatternSettings& settings) const; [[nodiscard]] META_PIMPL_API OptionMask GetOptions() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API tf::Executor& GetParallelExecutor() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API IObjectRegistry& GetObjectRegistry() const META_PIMPL_NOEXCEPT; + META_PIMPL_API bool UploadResources() const META_PIMPL_NOEXCEPT; META_PIMPL_API void RequestDeferredAction(DeferredAction action) const META_PIMPL_NOEXCEPT; META_PIMPL_API void CompleteInitialization() const; [[nodiscard]] META_PIMPL_API bool IsCompletingInitialization() const META_PIMPL_NOEXCEPT; @@ -120,11 +124,11 @@ class RenderContext // NOSONAR - class has more than 35 methods, constructors an [[nodiscard]] META_PIMPL_API bool ReadyToRender() const; META_PIMPL_API void Resize(const FrameSize& frame_size) const; META_PIMPL_API void Present() const; - [[nodiscard]] META_PIMPL_API Platform::AppView GetAppView() const; - [[nodiscard]] META_PIMPL_API const Settings& GetSettings() const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API uint32_t GetFrameBufferIndex() const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API uint32_t GetFrameIndex() const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API const IFpsCounter& GetFpsCounter() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API Platform::AppView GetAppView() const; + [[nodiscard]] META_PIMPL_API const Settings& GetSettings() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API uint32_t GetFrameBufferIndex() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API uint32_t GetFrameIndex() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API const Data::IFpsCounter& GetFpsCounter() const META_PIMPL_NOEXCEPT; META_PIMPL_API bool SetVSyncEnabled(bool vsync_enabled) const; META_PIMPL_API bool SetFrameBuffersCount(uint32_t frame_buffers_count) const; META_PIMPL_API bool SetFullScreen(bool is_full_screen) const; diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Sampler.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Sampler.h index df07bb5fd..5e0d52998 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Sampler.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Sampler.h @@ -36,6 +36,7 @@ namespace Methane::Graphics::Rhi { class RenderContext; +class ComputeContext; class ResourceBarriers; class Sampler // NOSONAR - constructors and assignment operators are required to use forward declared Impl and Ptr in header @@ -59,6 +60,7 @@ class Sampler // NOSONAR - constructors and assignment operators are required to META_PIMPL_API explicit Sampler(const Ptr& interface_ptr); META_PIMPL_API explicit Sampler(ISampler& interface_ref); META_PIMPL_API Sampler(const RenderContext& context, const Settings& settings); + META_PIMPL_API Sampler(const ComputeContext& context, const Settings& settings); META_PIMPL_API bool IsInitialized() const META_PIMPL_NOEXCEPT; META_PIMPL_API ISampler& GetInterface() const META_PIMPL_NOEXCEPT; @@ -83,7 +85,7 @@ class Sampler // NOSONAR - constructors and assignment operators are required to [[nodiscard]] META_PIMPL_API State GetState() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API ResourceUsageMask GetUsage() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API const DescriptorByViewId& GetDescriptorByViewId() const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API RenderContext GetRenderContext() const; + [[nodiscard]] META_PIMPL_API const IContext& GetContext() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API const Opt& GetOwnerQueueFamily() const META_PIMPL_NOEXCEPT; // Data::IEmitter interface methods diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Shader.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Shader.h index ba979123b..c44752368 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Shader.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Shader.h @@ -36,6 +36,7 @@ namespace Methane::Graphics::Rhi { class RenderContext; +class ComputeContext; class Shader // NOSONAR - constructors and assignment operators are required to use forward declared Impl and Ptr in header { @@ -53,6 +54,7 @@ class Shader // NOSONAR - constructors and assignment operators are required to META_PIMPL_API explicit Shader(const Ptr& interface_ptr); META_PIMPL_API explicit Shader(IShader& interface_ref); META_PIMPL_API Shader(Type type, const RenderContext& context, const Settings& settings); + META_PIMPL_API Shader(Type type, const ComputeContext& context, const Settings& settings); META_PIMPL_API bool IsInitialized() const META_PIMPL_NOEXCEPT; META_PIMPL_API IShader& GetInterface() const META_PIMPL_NOEXCEPT; diff --git a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Texture.h b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Texture.h index aa3cdae9e..facc49c74 100644 --- a/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Texture.h +++ b/Modules/Graphics/RHI/Impl/Include/Methane/Graphics/RHI/Texture.h @@ -38,6 +38,7 @@ namespace Methane::Graphics::Rhi { class RenderContext; +class ComputeContext; class ResourceBarriers; class CommandQueue; @@ -64,6 +65,7 @@ class Texture // NOSONAR - class has more than 35 methods, constructors and assi META_PIMPL_API explicit Texture(ITexture& interface_ref); META_PIMPL_API Texture(const IContext& context, const Settings& settings); META_PIMPL_API Texture(const RenderContext& render_context, const Settings& settings); + META_PIMPL_API Texture(const ComputeContext& compute_context, const Settings& settings); META_PIMPL_API bool IsInitialized() const META_PIMPL_NOEXCEPT; META_PIMPL_API ITexture& GetInterface() const META_PIMPL_NOEXCEPT; @@ -82,18 +84,16 @@ class Texture // NOSONAR - class has more than 35 methods, constructors and assi META_PIMPL_API bool SetState(State state, Barriers& out_barriers) const; META_PIMPL_API bool SetOwnerQueueFamily(uint32_t family_index) const; META_PIMPL_API bool SetOwnerQueueFamily(uint32_t family_index, Barriers& out_barriers) const; - META_PIMPL_API void SetData(const SubResources& sub_resources, const CommandQueue& target_cmd_queue) const; META_PIMPL_API void RestoreDescriptorViews(const DescriptorByViewId& descriptor_by_view_id) const; - [[nodiscard]] META_PIMPL_API SubResource GetData(const SubResource::Index& sub_resource_index = SubResource::Index(), const BytesRangeOpt& data_range = {}) const; [[nodiscard]] META_PIMPL_API Data::Size GetDataSize(Data::MemoryState size_type = Data::MemoryState::Reserved) const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API Data::Size GetSubResourceDataSize(const SubResource::Index& sub_resource_index = SubResource::Index()) const; - [[nodiscard]] META_PIMPL_API const SubResource::Count& GetSubresourceCount() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API SubResource::Count GetSubresourceCount() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API ResourceType GetResourceType() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API State GetState() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API ResourceUsageMask GetUsage() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API const DescriptorByViewId& GetDescriptorByViewId() const META_PIMPL_NOEXCEPT; - [[nodiscard]] META_PIMPL_API RenderContext GetRenderContext() const; + [[nodiscard]] META_PIMPL_API const IContext& GetContext() const META_PIMPL_NOEXCEPT; [[nodiscard]] META_PIMPL_API const Opt& GetOwnerQueueFamily() const META_PIMPL_NOEXCEPT; // Data::IEmitter interface methods @@ -102,6 +102,10 @@ class Texture // NOSONAR - class has more than 35 methods, constructors and assi // ITexture interface methods [[nodiscard]] META_PIMPL_API const Settings& GetSettings() const META_PIMPL_NOEXCEPT; + [[nodiscard]] META_PIMPL_API SubResource GetData(const CommandQueue& target_cmd_queue, + const SubResource::Index& sub_resource_index = SubResource::Index(), + const BytesRangeOpt& data_range = {}) const; + META_PIMPL_API void SetData(const CommandQueue& target_cmd_queue, const SubResources& sub_resources) const; private: using Impl = Methane::Graphics::META_GFX_NAME::Texture; diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Buffer.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Buffer.cpp index 4f9d05e63..becd2e188 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Buffer.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Buffer.cpp @@ -23,8 +23,9 @@ Methane Buffer PIMPL wrappers for direct calls to final implementation. #include #include -#include #include +#include +#include #ifdef META_GFX_METAL #include @@ -55,6 +56,16 @@ Buffer::Buffer(const IContext& context, const BufferSettings& settings) { } +Buffer::Buffer(const RenderContext& context, const BufferSettings& settings) + : Buffer(context.GetInterface(), settings) +{ +} + +Buffer::Buffer(const ComputeContext& context, const BufferSettings& settings) + : Buffer(context.GetInterface(), settings) +{ +} + bool Buffer::IsInitialized() const META_PIMPL_NOEXCEPT { return static_cast(m_impl_ptr); @@ -122,9 +133,9 @@ bool Buffer::SetOwnerQueueFamily(uint32_t family_index, Barriers& out_barriers) return state_changed; } -void Buffer::SetData(const SubResources& sub_resources, const CommandQueue& target_cmd_queue) const +void Buffer::SetData(const CommandQueue& target_cmd_queue, const SubResource& sub_resource) const { - GetImpl(m_impl_ptr).SetData(sub_resources, target_cmd_queue.GetInterface()); + GetImpl(m_impl_ptr).SetData(target_cmd_queue.GetInterface(), sub_resource); } void Buffer::RestoreDescriptorViews(const DescriptorByViewId& descriptor_by_view_id) const @@ -132,9 +143,9 @@ void Buffer::RestoreDescriptorViews(const DescriptorByViewId& descriptor_by_view GetImpl(m_impl_ptr).RestoreDescriptorViews(descriptor_by_view_id); } -SubResource Buffer::GetData(const SubResource::Index& sub_resource_index, const BytesRangeOpt& data_range) const +SubResource Buffer::GetData(const Rhi::CommandQueue& target_cmd_queue, const BytesRangeOpt& data_range) const { - return GetImpl(m_impl_ptr).GetData(sub_resource_index, data_range); + return GetImpl(m_impl_ptr).GetData(target_cmd_queue.GetInterface(), data_range); } Data::Size Buffer::GetDataSize(Data::MemoryState size_type) const META_PIMPL_NOEXCEPT @@ -142,16 +153,6 @@ Data::Size Buffer::GetDataSize(Data::MemoryState size_type) const META_PIMPL_NOE return GetImpl(m_impl_ptr).GetDataSize(size_type); } -Data::Size Buffer::GetSubResourceDataSize(const SubResource::Index& sub_resource_index) const -{ - return GetImpl(m_impl_ptr).GetSubResourceDataSize(sub_resource_index); -} - -const SubResource::Count& Buffer::GetSubresourceCount() const META_PIMPL_NOEXCEPT -{ - return GetImpl(m_impl_ptr).GetSubresourceCount(); -} - ResourceType Buffer::GetResourceType() const META_PIMPL_NOEXCEPT { return GetImpl(m_impl_ptr).GetResourceType(); diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/CommandKit.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/CommandKit.cpp index 1c858452f..aaf9ecc72 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/CommandKit.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/CommandKit.cpp @@ -21,10 +21,13 @@ Methane CommandKit PIMPL wrappers for direct calls to final implementation. ******************************************************************************/ +#include "Methane/Graphics/RHI/ICommandQueue.h" #include #include #include #include +#include +#include #include #include @@ -129,6 +132,30 @@ RenderCommandList CommandKit::GetRenderListForEncoding(CommandListId cmd_list_id return RenderCommandList(dynamic_cast(GetImpl(m_impl_ptr).GetListForEncoding(cmd_list_id, debug_group_name))); } +ComputeCommandList CommandKit::GetComputeList(CommandListId cmd_list_id) const +{ + META_CHECK_ARG_EQUAL(GetListType(), CommandListType::Compute); + return ComputeCommandList(dynamic_cast(GetImpl(m_impl_ptr).GetList(cmd_list_id))); +} + +ComputeCommandList CommandKit::GetComputeListForEncoding(CommandListId cmd_list_id, std::string_view debug_group_name) const +{ + META_CHECK_ARG_EQUAL(GetListType(), CommandListType::Compute); + return ComputeCommandList(dynamic_cast(GetImpl(m_impl_ptr).GetListForEncoding(cmd_list_id, debug_group_name))); +} + +TransferCommandList CommandKit::GetTransferList(CommandListId cmd_list_id) const +{ + META_CHECK_ARG_EQUAL(GetListType(), CommandListType::Transfer); + return TransferCommandList(dynamic_cast(GetImpl(m_impl_ptr).GetList(cmd_list_id))); +} + +TransferCommandList CommandKit::GetTransferListForEncoding(CommandListId cmd_list_id, std::string_view debug_group_name) const +{ + META_CHECK_ARG_EQUAL(GetListType(), CommandListType::Transfer); + return TransferCommandList(dynamic_cast(GetImpl(m_impl_ptr).GetListForEncoding(cmd_list_id, debug_group_name))); +} + CommandListSet CommandKit::GetListSet(const std::vector& cmd_list_ids, Opt frame_index_opt) const { return CommandListSet(GetImpl(m_impl_ptr).GetListSet(cmd_list_ids, frame_index_opt)); @@ -139,4 +166,14 @@ IFence& CommandKit::GetFence(CommandListId fence_id) const return GetImpl(m_impl_ptr).GetFence(fence_id); } +void CommandKit::ExecuteListSet(const std::vector& cmd_list_ids, Opt frame_index_opt) const +{ + GetImpl(m_impl_ptr).ExecuteListSet(cmd_list_ids, frame_index_opt); +} + +void CommandKit::ExecuteListSetAndWaitForCompletion(const std::vector& cmd_list_ids, Opt frame_index_opt) const +{ + GetImpl(m_impl_ptr).ExecuteListSetAndWaitForCompletion(cmd_list_ids, frame_index_opt); +} + } // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/CommandQueue.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/CommandQueue.cpp index 614b6b9df..6c789f52f 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/CommandQueue.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/CommandQueue.cpp @@ -23,6 +23,7 @@ Methane CommandQueue PIMPL wrappers for direct calls to final implementation. #include #include +#include #include #include #include @@ -30,6 +31,7 @@ Methane CommandQueue PIMPL wrappers for direct calls to final implementation. #include #include #include +#include #include @@ -60,6 +62,11 @@ CommandQueue::CommandQueue(const RenderContext& context, CommandListType command { } +CommandQueue::CommandQueue(const ComputeContext& context, CommandListType command_lists_type) + : CommandQueue(ICommandQueue::Create(context.GetInterface(), command_lists_type)) +{ +} + bool CommandQueue::IsInitialized() const META_PIMPL_NOEXCEPT { return static_cast(m_impl_ptr); @@ -110,6 +117,11 @@ TransferCommandList CommandQueue::CreateTransferCommandList() const return TransferCommandList(GetImpl(m_impl_ptr).CreateTransferCommandList()); } +ComputeCommandList CommandQueue::CreateComputeCommandList() const +{ + return ComputeCommandList(GetImpl(m_impl_ptr).CreateComputeCommandList()); +} + RenderCommandList CommandQueue::CreateRenderCommandList(const RenderPass& render_pass) const { return RenderCommandList(GetImpl(m_impl_ptr).CreateRenderCommandList(render_pass.GetInterface())); diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/ComputeCommandList.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/ComputeCommandList.cpp new file mode 100644 index 000000000..b35a687f7 --- /dev/null +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/ComputeCommandList.cpp @@ -0,0 +1,181 @@ +/****************************************************************************** + +Copyright 2022 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/ComputeCommandList.cpp +Methane ComputeCommandList PIMPL wrappers for direct calls to final implementation. + +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#ifdef META_GFX_METAL +#include +#else +#include +#endif + +namespace Methane::Graphics::Rhi +{ + +META_PIMPL_DEFAULT_CONSTRUCT_METHODS_IMPLEMENT(ComputeCommandList); +META_PIMPL_METHODS_COMPARE_IMPLEMENT(ComputeCommandList); + +ComputeCommandList::ComputeCommandList(const Ptr& interface_ptr) + : m_impl_ptr(std::dynamic_pointer_cast(interface_ptr)) +{ +} + +ComputeCommandList::ComputeCommandList(IComputeCommandList& interface_ref) + : ComputeCommandList(interface_ref.GetDerivedPtr()) +{ +} + +ComputeCommandList::ComputeCommandList(const CommandQueue& command_queue) + : ComputeCommandList(IComputeCommandList::Create(command_queue.GetInterface())) +{ +} + +bool ComputeCommandList::IsInitialized() const META_PIMPL_NOEXCEPT +{ + return static_cast(m_impl_ptr); +} + +IComputeCommandList& ComputeCommandList::GetInterface() const META_PIMPL_NOEXCEPT +{ + return *m_impl_ptr; +} + +Ptr ComputeCommandList::GetInterfacePtr() const META_PIMPL_NOEXCEPT +{ + return m_impl_ptr; +} + +bool ComputeCommandList::SetName(std::string_view name) const +{ + return GetImpl(m_impl_ptr).SetName(name); +} + +std::string_view ComputeCommandList::GetName() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).GetName(); +} + +void ComputeCommandList::Connect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Connect(receiver); +} + +void ComputeCommandList::Disconnect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Disconnect(receiver); +} + +void ComputeCommandList::PushDebugGroup(const DebugGroup& debug_group) const +{ + GetImpl(m_impl_ptr).PushDebugGroup(debug_group.GetInterface()); +} + +void ComputeCommandList::PopDebugGroup() const +{ + GetImpl(m_impl_ptr).PopDebugGroup(); +} + +void ComputeCommandList::Reset(const DebugGroup* debug_group_ptr) const +{ + GetImpl(m_impl_ptr).Reset(debug_group_ptr ? debug_group_ptr->GetInterfacePtr().get() : nullptr); +} + +void ComputeCommandList::ResetOnce(const DebugGroup* debug_group_ptr) const +{ + GetImpl(m_impl_ptr).ResetOnce(debug_group_ptr ? debug_group_ptr->GetInterfacePtr().get() : nullptr); +} + +void ComputeCommandList::SetProgramBindings(const ProgramBindings& program_bindings, ProgramBindingsApplyBehaviorMask apply_behavior) const +{ + GetImpl(m_impl_ptr).SetProgramBindings(program_bindings.GetInterface(), apply_behavior); +} + +void ComputeCommandList::SetResourceBarriers(const IResourceBarriers& resource_barriers) const +{ + GetImpl(m_impl_ptr).SetResourceBarriers(resource_barriers); +} + +void ComputeCommandList::Commit() const +{ + GetImpl(m_impl_ptr).Commit(); +} + +void ComputeCommandList::WaitUntilCompleted(uint32_t timeout_ms) const +{ + GetImpl(m_impl_ptr).WaitUntilCompleted(timeout_ms); +} + +Data::TimeRange ComputeCommandList::GetGpuTimeRange(bool in_cpu_nanoseconds) const +{ + return GetImpl(m_impl_ptr).GetGpuTimeRange(in_cpu_nanoseconds); +} + +CommandListState ComputeCommandList::GetState() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).GetState(); +} + +CommandQueue ComputeCommandList::GetCommandQueue() const +{ + return CommandQueue(GetImpl(m_impl_ptr).GetCommandQueue()); +} + +void ComputeCommandList::Connect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Connect(receiver); +} + +void ComputeCommandList::Disconnect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Disconnect(receiver); +} + +void ComputeCommandList::ResetWithState(const ComputeState& compute_state, const DebugGroup* debug_group_ptr) const +{ + GetImpl(m_impl_ptr).ResetWithState(compute_state.GetInterface(), + debug_group_ptr ? debug_group_ptr->GetInterfacePtr().get() : nullptr); +} + +void ComputeCommandList::ResetWithStateOnce(const ComputeState& compute_state, const DebugGroup* debug_group_ptr) const +{ + GetImpl(m_impl_ptr).ResetWithStateOnce(compute_state.GetInterface(), + debug_group_ptr ? debug_group_ptr->GetInterfacePtr().get() : nullptr); +} + +void ComputeCommandList::SetComputeState(const ComputeState& compute_state) const +{ + GetImpl(m_impl_ptr).SetComputeState(compute_state.GetInterface()); +} + +void ComputeCommandList::Dispatch(const ThreadGroupsCount& thread_groups_count) const +{ + GetImpl(m_impl_ptr).Dispatch(thread_groups_count); +} + +} // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/ComputeContext.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/ComputeContext.cpp new file mode 100644 index 000000000..6563f17d7 --- /dev/null +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/ComputeContext.cpp @@ -0,0 +1,229 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/ComputeContext.cpp +Methane ComputeContext PIMPL wrappers for direct calls to final implementation. + +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef META_GFX_METAL +#include +#else +#include +#endif + +namespace Methane::Graphics::Rhi +{ + +META_PIMPL_DEFAULT_CONSTRUCT_METHODS_IMPLEMENT(ComputeContext); +META_PIMPL_METHODS_COMPARE_IMPLEMENT(ComputeContext); + +ComputeContext::ComputeContext(const Ptr& interface_ptr) + : m_impl_ptr(std::dynamic_pointer_cast(interface_ptr)) +{ +} + +ComputeContext::ComputeContext(IComputeContext& render_context) + : ComputeContext(render_context.GetDerivedPtr()) +{ +} + +ComputeContext::ComputeContext(const Device& device, tf::Executor& parallel_executor, const Settings& settings) + : ComputeContext(IComputeContext::Create(device.GetInterface(), parallel_executor, settings)) +{ +} + +bool ComputeContext::IsInitialized() const META_PIMPL_NOEXCEPT +{ + return static_cast(m_impl_ptr); +} + +IComputeContext& ComputeContext::GetInterface() const META_PIMPL_NOEXCEPT +{ + return *m_impl_ptr; +} + +Ptr ComputeContext::GetInterfacePtr() const META_PIMPL_NOEXCEPT +{ + return m_impl_ptr; +} + +bool ComputeContext::SetName(std::string_view name) const +{ + return GetImpl(m_impl_ptr).SetName(name); +} + +std::string_view ComputeContext::GetName() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).GetName(); +} + +void ComputeContext::Connect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Connect(receiver); +} + +void ComputeContext::Disconnect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Disconnect(receiver); +} + +CommandQueue ComputeContext::CreateCommandQueue(CommandListType type) const +{ + return CommandQueue(GetImpl(m_impl_ptr).CreateCommandQueue(type)); +} + +CommandKit ComputeContext::CreateCommandKit(CommandListType type) const +{ + return CommandKit(GetImpl(m_impl_ptr).CreateCommandKit(type)); +} + +Shader ComputeContext::CreateShader(ShaderType type, const ShaderSettings& settings) const +{ + return Shader(GetImpl(m_impl_ptr).CreateShader(type, settings)); +} + +Program ComputeContext::CreateProgram(const ProgramSettingsImpl& settings) const +{ + return Program(GetImpl(m_impl_ptr).CreateProgram(ProgramSettingsImpl::Convert(GetInterface(), settings))); +} + +ComputeState ComputeContext::CreateComputeState(const ComputeStateSettingsImpl& settings) const +{ + return ComputeState(GetImpl(m_impl_ptr).CreateComputeState(ComputeStateSettingsImpl::Convert(settings))); +} + +Buffer ComputeContext::CreateBuffer(const BufferSettings& settings) const +{ + return Buffer(GetImpl(m_impl_ptr).CreateBuffer(settings)); +} + +Texture ComputeContext::CreateTexture(const TextureSettings& settings) const +{ + return Texture(GetImpl(m_impl_ptr).CreateTexture(settings)); +} + +Sampler ComputeContext::CreateSampler(const SamplerSettings& settings) const +{ + return Sampler(GetImpl(m_impl_ptr).CreateSampler(settings)); +} + +ContextOptionMask ComputeContext::GetOptions() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).GetOptions(); +} + +tf::Executor& ComputeContext::GetParallelExecutor() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).GetParallelExecutor(); +} + +IObjectRegistry& ComputeContext::GetObjectRegistry() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).GetObjectRegistry(); +} + +bool ComputeContext::UploadResources() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).UploadResources(); +} + +void ComputeContext::RequestDeferredAction(DeferredAction action) const META_PIMPL_NOEXCEPT +{ + GetImpl(m_impl_ptr).RequestDeferredAction(action); +} + +void ComputeContext::CompleteInitialization() const +{ + GetImpl(m_impl_ptr).CompleteInitialization(); +} + +bool ComputeContext::IsCompletingInitialization() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).IsCompletingInitialization(); +} + +void ComputeContext::WaitForGpu(WaitFor wait_for) const +{ + GetImpl(m_impl_ptr).WaitForGpu(wait_for); +} + +void ComputeContext::Reset(const Device& device) const +{ + GetImpl(m_impl_ptr).Reset(device.GetInterface()); +} + +void ComputeContext::Reset() const +{ + GetImpl(m_impl_ptr).Reset(); +} + +Device ComputeContext::GetDevice() const +{ + return Device(const_cast(GetImpl(m_impl_ptr).GetDevice())); +} + +CommandKit ComputeContext::GetDefaultCommandKit(CommandListType type) const +{ + return CommandKit(GetImpl(m_impl_ptr).GetDefaultCommandKit(type)); +} + +CommandKit ComputeContext::GetDefaultCommandKit(const CommandQueue& cmd_queue) const +{ + return CommandKit(GetImpl(m_impl_ptr).GetDefaultCommandKit(cmd_queue.GetInterface())); +} + +CommandKit ComputeContext::GetUploadCommandKit() const +{ + return CommandKit(GetImpl(m_impl_ptr).GetUploadCommandKit()); +} + +CommandKit ComputeContext::GetComputeCommandKit() const +{ + return CommandKit(GetImpl(m_impl_ptr).GetComputeCommandKit()); +} + +void ComputeContext::Connect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Connect(receiver); +} + +void ComputeContext::Disconnect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Disconnect(receiver); +} + +const ComputeContextSettings& ComputeContext::GetSettings() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).GetSettings(); +} + +} // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/ComputeState.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/ComputeState.cpp new file mode 100644 index 000000000..aa9ca2cd2 --- /dev/null +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/ComputeState.cpp @@ -0,0 +1,126 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/ComputeState.cpp +Methane ComputeState PIMPL wrappers for direct calls to final implementation. + +******************************************************************************/ + +#include +#include +#include + +#include + +#ifdef META_GFX_METAL +#include +#else +#include +#endif + +namespace Methane::Graphics::Rhi +{ + +ComputeStateSettings ComputeStateSettingsImpl::Convert(const ComputeStateSettingsImpl& settings) +{ + return ComputeStateSettings + { + settings.program.GetInterfacePtr(), + settings.thread_group_size + }; +} + +META_PIMPL_DEFAULT_CONSTRUCT_METHODS_IMPLEMENT(ComputeState); +META_PIMPL_METHODS_COMPARE_IMPLEMENT(ComputeState); + +ComputeState::ComputeState(const Ptr& interface_ptr) + : m_impl_ptr(std::dynamic_pointer_cast(interface_ptr)) +{ +} + +ComputeState::ComputeState(IComputeState& interface_ref) + : ComputeState(interface_ref.GetDerivedPtr()) +{ +} + +ComputeState::ComputeState(const RenderContext& context, const Settings& settings) + : ComputeState(IComputeState::Create(context.GetInterface(), ComputeStateSettingsImpl::Convert(settings))) +{ +} + +ComputeState::ComputeState(const ComputeContext& context, const Settings& settings) + : ComputeState(IComputeState::Create(context.GetInterface(), ComputeStateSettingsImpl::Convert(settings))) +{ +} + +bool ComputeState::IsInitialized() const META_PIMPL_NOEXCEPT +{ + return static_cast(m_impl_ptr); +} + +IComputeState& ComputeState::GetInterface() const META_PIMPL_NOEXCEPT +{ + return *m_impl_ptr; +} + +Ptr ComputeState::GetInterfacePtr() const META_PIMPL_NOEXCEPT +{ + return m_impl_ptr; +} + +bool ComputeState::SetName(std::string_view name) const +{ + return GetImpl(m_impl_ptr).SetName(name); +} + +std::string_view ComputeState::GetName() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).GetName(); +} + +void ComputeState::Connect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Connect(receiver); +} + +void ComputeState::Disconnect(Data::Receiver& receiver) const +{ + GetImpl(m_impl_ptr).Data::Emitter::Disconnect(receiver); +} + +const ComputeStateSettings& ComputeState::GetSettings() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).GetSettings(); +} + +void ComputeState::Reset(const Settings& settings) const +{ + return GetImpl(m_impl_ptr).Reset(ComputeStateSettingsImpl::Convert(settings)); +} + +void ComputeState::Reset(const IComputeState::Settings& settings) const +{ + return GetImpl(m_impl_ptr).Reset(settings); +} + +Program ComputeState::GetProgram() const +{ + return Program(GetSettings().program_ptr); +} + +} // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Device.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Device.cpp index 6c99f7cf5..6344f53a7 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Device.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Device.cpp @@ -23,6 +23,7 @@ Methane Device PIMPL wrappers for direct calls to final implementation. #include #include +#include #include @@ -39,6 +40,7 @@ namespace Methane::Graphics::Rhi { META_PIMPL_METHODS_IMPLEMENT(Device); +META_PIMPL_METHODS_COMPARE_IMPLEMENT(Device); Device::Device(const Ptr& interface_ptr) : m_impl_ptr(std::dynamic_pointer_cast(interface_ptr)) @@ -75,6 +77,11 @@ RenderContext Device::CreateRenderContext(const Platform::AppEnvironment& env, t return RenderContext(GetImpl(m_impl_ptr).CreateRenderContext(env, parallel_executor, settings)); } +ComputeContext Device::CreateComputeContext(tf::Executor& parallel_executor, const ComputeContextSettings& settings) const +{ + return ComputeContext(GetImpl(m_impl_ptr).CreateComputeContext(parallel_executor, settings)); +} + const std::string& Device::GetAdapterName() const META_PIMPL_NOEXCEPT { return GetImpl(m_impl_ptr).GetAdapterName(); diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Fence.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Fence.cpp index 4fdb80bce..99fcdeeae 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Fence.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Fence.cpp @@ -98,9 +98,9 @@ void Fence::WaitOnCpu() const GetImpl(m_impl_ptr).WaitOnCpu(); } -void Fence::WaitOnGpu(ICommandQueue& wait_on_command_queue) const +void Fence::WaitOnGpu(const CommandQueue& wait_on_command_queue) const { - GetImpl(m_impl_ptr).WaitOnGpu(wait_on_command_queue); + GetImpl(m_impl_ptr).WaitOnGpu(wait_on_command_queue.GetInterface()); } void Fence::FlushOnCpu() const @@ -108,9 +108,9 @@ void Fence::FlushOnCpu() const GetImpl(m_impl_ptr).FlushOnCpu(); } -void Fence::FlushOnGpu(ICommandQueue& wait_on_command_queue) const +void Fence::FlushOnGpu(const CommandQueue& wait_on_command_queue) const { - GetImpl(m_impl_ptr).FlushOnGpu(wait_on_command_queue); + GetImpl(m_impl_ptr).FlushOnGpu(wait_on_command_queue.GetInterface()); } } // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Program.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Program.cpp index 40fd041d1..66a4a1f04 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Program.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Program.cpp @@ -23,6 +23,7 @@ Methane Program PIMPL wrappers for direct calls to final implementation. #include #include +#include #include #include @@ -74,6 +75,11 @@ Program::Program(const RenderContext& context, const Settings& settings) { } +Program::Program(const ComputeContext& context, const Settings& settings) + : Program(IProgram::Create(context.GetInterface(), Settings::Convert(context.GetInterface(), settings))) +{ +} + bool Program::IsInitialized() const META_PIMPL_NOEXCEPT { return static_cast(m_impl_ptr); diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/RenderContext.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/RenderContext.cpp index 8e7ce8643..eca2ef67c 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/RenderContext.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/RenderContext.cpp @@ -32,6 +32,7 @@ Methane RenderContext PIMPL wrappers for direct calls to final implementation. #include #include #include +#include #include @@ -137,6 +138,11 @@ RenderState RenderContext::CreateRenderState(const RenderStateSettingsImpl& sett return RenderState(GetImpl(m_impl_ptr).CreateRenderState(RenderStateSettingsImpl::Convert(settings))); } +ComputeState RenderContext::CreateComputeState(const ComputeStateSettingsImpl& settings) const +{ + return ComputeState(GetImpl(m_impl_ptr).CreateComputeState(ComputeStateSettingsImpl::Convert(settings))); +} + RenderPattern RenderContext::CreateRenderPattern(const RenderPatternSettings& settings) const { return RenderPattern(GetImpl(m_impl_ptr).CreateRenderPattern(settings)); @@ -157,6 +163,11 @@ IObjectRegistry& RenderContext::GetObjectRegistry() const META_PIMPL_NOEXCEPT return GetImpl(m_impl_ptr).GetObjectRegistry(); } +bool RenderContext::UploadResources() const META_PIMPL_NOEXCEPT +{ + return GetImpl(m_impl_ptr).UploadResources(); +} + void RenderContext::RequestDeferredAction(DeferredAction action) const META_PIMPL_NOEXCEPT { GetImpl(m_impl_ptr).RequestDeferredAction(action); @@ -257,7 +268,7 @@ uint32_t RenderContext::GetFrameIndex() const META_PIMPL_NOEXCEPT return GetImpl(m_impl_ptr).GetFrameIndex(); } -const IFpsCounter& RenderContext::GetFpsCounter() const META_PIMPL_NOEXCEPT +const Data::IFpsCounter& RenderContext::GetFpsCounter() const META_PIMPL_NOEXCEPT { return GetImpl(m_impl_ptr).GetFpsCounter(); } diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Sampler.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Sampler.cpp index 51104b4be..46a3fc71d 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Sampler.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Sampler.cpp @@ -23,6 +23,7 @@ Methane Sampler PIMPL wrappers for direct calls to final implementation. #include #include +#include #include #include @@ -54,6 +55,11 @@ Sampler::Sampler(const RenderContext& context, const Settings& settings) { } +Sampler::Sampler(const ComputeContext& context, const Settings& settings) + : Sampler(ISampler::Create(context.GetInterface(), settings)) +{ +} + bool Sampler::IsInitialized() const META_PIMPL_NOEXCEPT { return static_cast(m_impl_ptr); @@ -146,11 +152,9 @@ const Sampler::DescriptorByViewId& Sampler::GetDescriptorByViewId() const META_P return GetImpl(m_impl_ptr).GetDescriptorByViewId(); } -RenderContext Sampler::GetRenderContext() const +const IContext& Sampler::GetContext() const META_PIMPL_NOEXCEPT { - IContext& context = const_cast(GetImpl(m_impl_ptr).GetContext()); // NOSONAR - META_CHECK_ARG_EQUAL(context.GetType(), ContextType::Render); - return RenderContext(dynamic_cast(context)); + return GetImpl(m_impl_ptr).GetContext(); } const Opt& Sampler::GetOwnerQueueFamily() const META_PIMPL_NOEXCEPT diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Shader.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Shader.cpp index 13718dc26..5fbb8712d 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Shader.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Shader.cpp @@ -23,6 +23,7 @@ Methane Shader PIMPL wrappers for direct calls to final implementation. #include #include +#include #include @@ -53,6 +54,11 @@ Shader::Shader(Type type, const RenderContext& context, const Settings& settings { } +Shader::Shader(Type type, const ComputeContext& context, const Settings& settings) + : Shader(IShader::Create(type, context.GetInterface(), settings)) +{ +} + bool Shader::IsInitialized() const META_PIMPL_NOEXCEPT { return static_cast(m_impl_ptr); diff --git a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Texture.cpp b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Texture.cpp index b3b4807c2..0063207cf 100644 --- a/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Texture.cpp +++ b/Modules/Graphics/RHI/Impl/Sources/Methane/Graphics/RHI/Texture.cpp @@ -24,6 +24,7 @@ Methane Texture PIMPL wrappers for direct calls to final implementation. #include #include #include +#include #include #include @@ -60,6 +61,11 @@ Texture::Texture(const RenderContext& render_context, const Settings& settings) { } +Texture::Texture(const ComputeContext& compute_context, const Settings& settings) + : Texture(compute_context.GetInterface(), settings) +{ +} + bool Texture::IsInitialized() const META_PIMPL_NOEXCEPT { return static_cast(m_impl_ptr); @@ -127,9 +133,9 @@ bool Texture::SetOwnerQueueFamily(uint32_t family_index, Barriers& out_barriers) return state_changed; } -void Texture::SetData(const SubResources& sub_resources, const CommandQueue& target_cmd_queue) const +void Texture::SetData(const CommandQueue& target_cmd_queue, const SubResources& sub_resources) const { - GetImpl(m_impl_ptr).SetData(sub_resources, target_cmd_queue.GetInterface()); + GetImpl(m_impl_ptr).SetData(target_cmd_queue.GetInterface(), sub_resources); } void Texture::RestoreDescriptorViews(const DescriptorByViewId& descriptor_by_view_id) const @@ -137,9 +143,9 @@ void Texture::RestoreDescriptorViews(const DescriptorByViewId& descriptor_by_vie GetImpl(m_impl_ptr).RestoreDescriptorViews(descriptor_by_view_id); } -SubResource Texture::GetData(const SubResource::Index& sub_resource_index, const BytesRangeOpt& data_range) const +SubResource Texture::GetData(const CommandQueue& target_cmd_queue, const SubResource::Index& sub_resource_index, const BytesRangeOpt& data_range) const { - return GetImpl(m_impl_ptr).GetData(sub_resource_index, data_range); + return GetImpl(m_impl_ptr).GetData(target_cmd_queue.GetInterface(), sub_resource_index, data_range); } Data::Size Texture::GetDataSize(Data::MemoryState size_type) const META_PIMPL_NOEXCEPT @@ -152,7 +158,7 @@ Data::Size Texture::GetSubResourceDataSize(const SubResource::Index& sub_resourc return GetImpl(m_impl_ptr).GetSubResourceDataSize(sub_resource_index); } -const SubResource::Count& Texture::GetSubresourceCount() const META_PIMPL_NOEXCEPT +SubResource::Count Texture::GetSubresourceCount() const META_PIMPL_NOEXCEPT { return GetImpl(m_impl_ptr).GetSubresourceCount(); } @@ -177,11 +183,9 @@ const Texture::DescriptorByViewId& Texture::GetDescriptorByViewId() const META_P return GetImpl(m_impl_ptr).GetDescriptorByViewId(); } -RenderContext Texture::GetRenderContext() const +const IContext& Texture::GetContext() const META_PIMPL_NOEXCEPT { - IContext& context = const_cast(GetImpl(m_impl_ptr).GetContext()); // NOSONAR - META_CHECK_ARG_EQUAL(context.GetType(), ContextType::Render); - return RenderContext(dynamic_cast(context)); + return GetImpl(m_impl_ptr).GetContext(); } const Opt& Texture::GetOwnerQueueFamily() const META_PIMPL_NOEXCEPT diff --git a/Modules/Graphics/RHI/Interface/CMakeLists.txt b/Modules/Graphics/RHI/Interface/CMakeLists.txt index c9313ea17..ad56fc0da 100644 --- a/Modules/Graphics/RHI/Interface/CMakeLists.txt +++ b/Modules/Graphics/RHI/Interface/CMakeLists.txt @@ -10,12 +10,14 @@ list(APPEND HEADERS ${INCLUDE_DIR}/ISystem.h ${INCLUDE_DIR}/IContext.h ${INCLUDE_DIR}/IRenderContext.h + ${INCLUDE_DIR}/IComputeContext.h ${INCLUDE_DIR}/IFence.h ${INCLUDE_DIR}/IShader.h ${INCLUDE_DIR}/IProgram.h ${INCLUDE_DIR}/IProgramBindings.h ${INCLUDE_DIR}/IRenderState.h ${INCLUDE_DIR}/IViewState.h + ${INCLUDE_DIR}/IComputeState.h ${INCLUDE_DIR}/IResource.h ${INCLUDE_DIR}/IResourceBarriers.h ${INCLUDE_DIR}/ResourceView.h @@ -31,11 +33,12 @@ list(APPEND HEADERS ${INCLUDE_DIR}/ICommandListDebugGroup.h ${INCLUDE_DIR}/ICommandListSet.h ${INCLUDE_DIR}/ITransferCommandList.h + ${INCLUDE_DIR}/IComputeCommandList.h ${INCLUDE_DIR}/IRenderCommandList.h ${INCLUDE_DIR}/IParallelRenderCommandList.h ${INCLUDE_DIR}/IQueryPool.h ${INCLUDE_DIR}/IDescriptorManager.h - ${INCLUDE_DIR}/IFpsCounter.h + ${INCLUDE_DIR}/TypeFormatters.hpp ) list(APPEND SOURCES @@ -45,11 +48,13 @@ list(APPEND SOURCES ${SOURCES_DIR}/IFence.cpp ${SOURCES_DIR}/IContext.cpp ${SOURCES_DIR}/IRenderContext.cpp + ${SOURCES_DIR}/IComputeContext.cpp ${SOURCES_DIR}/IShader.cpp ${SOURCES_DIR}/IProgram.cpp ${SOURCES_DIR}/IProgramBindings.cpp ${SOURCES_DIR}/IRenderState.cpp ${SOURCES_DIR}/IViewState.cpp + ${SOURCES_DIR}/IComputeState.cpp ${SOURCES_DIR}/IResource.cpp ${SOURCES_DIR}/IResourceBarriers.cpp ${SOURCES_DIR}/IBuffer.cpp @@ -58,10 +63,10 @@ list(APPEND SOURCES ${SOURCES_DIR}/IQueryPool.cpp ${SOURCES_DIR}/IRenderPattern.cpp ${SOURCES_DIR}/IRenderPass.cpp - ${SOURCES_DIR}/IFpsCounter.cpp ${SOURCES_DIR}/ICommandKit.cpp ${SOURCES_DIR}/ICommandQueue.cpp ${SOURCES_DIR}/ITransferCommandList.cpp + ${SOURCES_DIR}/IComputeCommandList.cpp ${SOURCES_DIR}/IRenderCommandList.cpp ${SOURCES_DIR}/IParallelRenderCommandList.cpp ${SOURCES_DIR}/ResourceView.cpp @@ -78,11 +83,11 @@ target_link_libraries(${TARGET} MethaneDataProvider MethaneDataRangeSet MethaneDataEvents + MethaneDataPrimitives MethaneGraphicsTypes MethanePlatformAppView PRIVATE MethaneBuildOptions - MethaneDataPrimitives MethaneInstrumentation MethaneMathPrecompiledHeaders nowide diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IBuffer.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IBuffer.h index d758007a3..bd5d2e871 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IBuffer.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IBuffer.h @@ -61,6 +61,9 @@ struct BufferSettings [[nodiscard]] static BufferSettings ForIndexBuffer(Data::Size size, PixelFormat format, bool is_volatile = false); [[nodiscard]] static BufferSettings ForConstantBuffer(Data::Size size, bool addressable = false, bool is_volatile = false); [[nodiscard]] static BufferSettings ForReadBackBuffer(Data::Size size); + + bool operator==(const BufferSettings& other) const; + bool operator!=(const BufferSettings& other) const; }; struct IBuffer @@ -76,6 +79,8 @@ struct IBuffer // IBuffer interface [[nodiscard]] virtual const Settings& GetSettings() const noexcept = 0; [[nodiscard]] virtual uint32_t GetFormattedItemsCount() const noexcept = 0; + [[nodiscard]] virtual SubResource GetData(ICommandQueue& target_cmd_queue, const BytesRangeOpt& data_range = {}) = 0; + virtual void SetData(ICommandQueue& target_cmd_queue, const SubResource& sub_resource) = 0; }; } // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandKit.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandKit.h index 35f290769..fbc76cf28 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandKit.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandKit.h @@ -65,6 +65,8 @@ struct ICommandKit [[nodiscard]] virtual ICommandList& GetListForEncoding(CommandListId cmd_list_id = 0U, std::string_view debug_group_name = {}) const = 0; [[nodiscard]] virtual ICommandListSet& GetListSet(const std::vector& cmd_list_ids = { 0U }, Opt frame_index_opt = {}) const = 0; [[nodiscard]] virtual IFence& GetFence(CommandListId fence_id = 0U) const = 0; + virtual void ExecuteListSet(const std::vector& cmd_list_ids = { 0U }, Opt frame_index_opt = {}) const = 0; + virtual void ExecuteListSetAndWaitForCompletion(const std::vector& cmd_list_ids = { 0U }, Opt frame_index_opt = {}) const = 0; }; } // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandList.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandList.h index 644fdc9e6..b21d48f8e 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandList.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandList.h @@ -42,6 +42,7 @@ enum class CommandListType Transfer, Render, ParallelRender, + Compute, Count }; diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandQueue.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandQueue.h index efccc88e9..14421e59f 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandQueue.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ICommandQueue.h @@ -37,6 +37,7 @@ struct ICommandKit; struct IFence; struct IRenderPass; struct ITransferCommandList; +struct IComputeCommandList; struct IRenderCommandList; struct IParallelRenderCommandList; struct ITimestampQueryPool; @@ -51,6 +52,7 @@ struct ICommandQueue [[nodiscard]] virtual Ptr CreateCommandKit() = 0; [[nodiscard]] virtual Ptr CreateFence() = 0; [[nodiscard]] virtual Ptr CreateTransferCommandList() = 0; + [[nodiscard]] virtual Ptr CreateComputeCommandList() = 0; [[nodiscard]] virtual Ptr CreateRenderCommandList(IRenderPass& render_pass) = 0; [[nodiscard]] virtual Ptr CreateParallelRenderCommandList(IRenderPass& render_pass) = 0; [[nodiscard]] virtual Ptr CreateTimestampQueryPool(uint32_t max_timestamps_per_frame) = 0; diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IComputeCommandList.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IComputeCommandList.h new file mode 100644 index 000000000..359ac335b --- /dev/null +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IComputeCommandList.h @@ -0,0 +1,53 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/IComputeCommandList.h +Methane compute command list interface. + +******************************************************************************/ + +#pragma once + +#include "ICommandList.h" + +#include +#include + +namespace Methane::Graphics::Rhi +{ + +struct IComputeState; + +using ThreadGroupsCount = VolumeSize; + +struct IComputeCommandList + : virtual ICommandList // NOSONAR +{ + static constexpr Type type = Type::Compute; + + // Create IComputeCommandList instance + [[nodiscard]] static Ptr Create(ICommandQueue& command_queue); + + // IComputeCommandList interface + virtual void ResetWithState(IComputeState& compute_state, IDebugGroup* debug_group_ptr = nullptr) = 0; + virtual void ResetWithStateOnce(IComputeState& compute_state, IDebugGroup* debug_group_ptr = nullptr) = 0; + virtual void SetComputeState(IComputeState& compute_state) = 0; + virtual void Dispatch(const ThreadGroupsCount& thread_groups_count) = 0; +}; + +} // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IComputeContext.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IComputeContext.h new file mode 100644 index 000000000..18f04207c --- /dev/null +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IComputeContext.h @@ -0,0 +1,63 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/IComputeContext.h +Methane render context interface: represents graphics device and swap chain, +provides basic multi-frame rendering synchronization and frame presenting APIs. + +******************************************************************************/ + +#pragma once + +#include "IContext.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace Methane::Graphics::Rhi +{ + +struct ComputeContextSettings +{ + ContextOptionMask options{ }; + + bool operator==(const ComputeContextSettings& other) const noexcept; + bool operator!=(const ComputeContextSettings& other) const noexcept; +}; + +struct IComputeContext + : virtual IContext // NOSONAR +{ + using Settings = ComputeContextSettings; + + // Create IComputeContext instance + [[nodiscard]] static Ptr Create(IDevice& device, tf::Executor& parallel_executor, const Settings& settings); + + // IComputeContext interface + [[nodiscard]] virtual const Settings& GetSettings() const noexcept = 0; + + [[nodiscard]] ICommandKit& GetComputeCommandKit() const; +}; + +} // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IComputeState.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IComputeState.h new file mode 100644 index 000000000..9b03fd8e5 --- /dev/null +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IComputeState.h @@ -0,0 +1,65 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/IComputeState.h +Methane compute state interface: specifies configuration of the compute pipeline. + +******************************************************************************/ + +#pragma once + +#include "IObject.h" + +#include +#include +#include + +namespace Methane::Graphics::Rhi +{ + +struct IProgram; + +using ThreadGroupSize = VolumeSize; + +struct ComputeStateSettings +{ + Ptr program_ptr; + ThreadGroupSize thread_group_size; // Duplicated in HLSL attribute [numthreads(x,y,z)] of compute shader, but Metal does not use it + + [[nodiscard]] bool operator==(const ComputeStateSettings& other) const noexcept; + [[nodiscard]] bool operator!=(const ComputeStateSettings& other) const noexcept; + [[nodiscard]] explicit operator std::string() const; +}; + +struct IContext; + +struct IComputeState + : virtual IObject // NOSONAR +{ +public: + using Settings = ComputeStateSettings; + + // Create IComputeState instance + [[nodiscard]] static Ptr Create(const IContext& device, const Settings& state_settings); + + // IComputeState interface + [[nodiscard]] virtual const Settings& GetSettings() const noexcept = 0; + virtual void Reset(const Settings& settings) = 0; +}; + +} // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IContext.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IContext.h index 0272c1d5f..cf9013aa3 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IContext.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IContext.h @@ -44,11 +44,13 @@ namespace Methane::Graphics::Rhi enum class ContextType { Render, + Compute }; enum class ContextWaitFor { RenderComplete, + ComputeComplete, FramePresented, ResourcesUploaded }; @@ -62,8 +64,9 @@ enum class ContextDeferredAction : uint32_t enum class ContextOption : uint32_t { - TransferWithD3D12DirectQueue, // Transfer command lists and queues in DX API are created with DIRECT type instead of COPY type - EmulateD3D12RenderPass // Render passes are emulated with traditional DX API, instead of using native DX render pass API + DeferredProgramBindingsInitialization, // Defer program bindings initialization on GPU until Context::CompleteInitialization + TransferWithD3D12DirectQueue, // Transfer command lists and queues in DX API are created with DIRECT type instead of COPY type + EmulateD3D12RenderPass // Render passes are emulated with traditional DX API, instead of using native DX render pass API }; using ContextOptionMask = Data::EnumMask; @@ -91,12 +94,14 @@ struct ICommandQueue; struct ICommandKit; struct IShader; struct IProgram; +struct IComputeState; struct IBuffer; struct ITexture; struct ISampler; struct ShaderSettings; struct ProgramSettings; +struct ComputeStateSettings; struct BufferSettings; struct TextureSettings; struct SamplerSettings; @@ -120,6 +125,7 @@ struct IContext [[nodiscard]] virtual Ptr CreateCommandKit(CommandListType type) const = 0; [[nodiscard]] virtual Ptr CreateShader(ShaderType type, const ShaderSettings& settings) const = 0; [[nodiscard]] virtual Ptr CreateProgram(const ProgramSettings& settings) const = 0; + [[nodiscard]] virtual Ptr CreateComputeState(const ComputeStateSettings& settings) const = 0; [[nodiscard]] virtual Ptr CreateBuffer(const BufferSettings& settings) const = 0; [[nodiscard]] virtual Ptr CreateTexture(const TextureSettings& settings) const = 0; [[nodiscard]] virtual Ptr CreateSampler(const SamplerSettings& settings) const = 0; @@ -128,6 +134,7 @@ struct IContext [[nodiscard]] virtual tf::Executor& GetParallelExecutor() const noexcept = 0; [[nodiscard]] virtual IObjectRegistry& GetObjectRegistry() noexcept = 0; [[nodiscard]] virtual const IObjectRegistry& GetObjectRegistry() const noexcept = 0; + virtual bool UploadResources() const = 0; virtual void RequestDeferredAction(DeferredAction action) const noexcept = 0; virtual void CompleteInitialization() = 0; [[nodiscard]] virtual bool IsCompletingInitialization() const noexcept = 0; diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IDevice.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IDevice.h index 7736b2ad0..eb2615562 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IDevice.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IDevice.h @@ -56,13 +56,19 @@ using DeviceFeatureMask = Data::EnumMask; struct DeviceCaps { - DeviceFeatureMask features { ~0U }; + DeviceFeatureMask features{ + DeviceFeature::PresentToWindow, + DeviceFeature::AnisotropicFiltering, + DeviceFeature::ImageCubeArray, + }; uint32_t render_queues_count { 1U }; uint32_t transfer_queues_count { 1U }; + uint32_t compute_queues_count { 1U }; DeviceCaps& SetFeatures(DeviceFeatureMask new_features) noexcept; DeviceCaps& SetRenderQueuesCount(uint32_t new_render_queues_count) noexcept; DeviceCaps& SetTransferQueuesCount(uint32_t new_transfer_queues_count) noexcept; + DeviceCaps& SetComputeQueuesCount(uint32_t new_compute_queues_count) noexcept; }; struct IDevice; @@ -76,7 +82,9 @@ struct IDeviceCallback }; struct IRenderContext; +struct IComputeContext; struct RenderContextSettings; +struct ComputeContextSettings; struct IDevice : virtual IObject // NOSONAR @@ -86,11 +94,12 @@ struct IDevice using Feature = DeviceFeature; using Capabilities = DeviceCaps; - [[nodiscard]] virtual Ptr CreateRenderContext(const Platform::AppEnvironment& env, tf::Executor& parallel_executor, const RenderContextSettings& settings) = 0; - [[nodiscard]] virtual const std::string& GetAdapterName() const noexcept = 0; - [[nodiscard]] virtual bool IsSoftwareAdapter() const noexcept = 0; - [[nodiscard]] virtual const Capabilities& GetCapabilities() const noexcept = 0; - [[nodiscard]] virtual std::string ToString() const = 0; + [[nodiscard]] virtual Ptr CreateRenderContext(const Platform::AppEnvironment& env, tf::Executor& parallel_executor, const RenderContextSettings& settings) = 0; + [[nodiscard]] virtual Ptr CreateComputeContext(tf::Executor& parallel_executor, const ComputeContextSettings& settings) = 0; + [[nodiscard]] virtual const std::string& GetAdapterName() const noexcept = 0; + [[nodiscard]] virtual bool IsSoftwareAdapter() const noexcept = 0; + [[nodiscard]] virtual const Capabilities& GetCapabilities() const noexcept = 0; + [[nodiscard]] virtual std::string ToString() const = 0; }; } // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IProgram.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IProgram.h index de4b5e444..dadd09526 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IProgram.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IProgram.h @@ -93,6 +93,7 @@ class ProgramArgument [[nodiscard]] size_t GetHash() const noexcept { return m_hash; } [[nodiscard]] bool operator==(const ProgramArgument& other) const noexcept; + [[nodiscard]] bool operator<(const ProgramArgument& other) const noexcept; [[nodiscard]] virtual explicit operator std::string() const noexcept; private: diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IProgramBindings.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IProgramBindings.h index 84bce79e2..85d8fc150 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IProgramBindings.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IProgramBindings.h @@ -55,8 +55,8 @@ class ProgramArgumentConstantModificationException struct ProgramArgumentBindingSettings { - Rhi::ProgramArgumentAccessor argument; - IResource::Type resource_type; + ProgramArgumentAccessor argument; + ResourceType resource_type; uint32_t resource_count = 1; }; @@ -93,7 +93,7 @@ class ProgramBindingsUnboundArgumentsException: public std::runtime_error [[nodiscard]] const IProgram::Arguments& GetArguments() const noexcept { return m_unbound_arguments; } private: - const IProgram& m_program; + const IProgram& m_program; const IProgram::Arguments m_unbound_arguments; }; diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IRenderContext.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IRenderContext.h index b2ecc13d2..6dfd015fe 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IRenderContext.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IRenderContext.h @@ -25,8 +25,8 @@ provides basic multi-frame rendering synchronization and frame presenting APIs. #pragma once #include "IContext.h" -#include "IFpsCounter.h" +#include #include #include #include @@ -49,7 +49,7 @@ struct RenderContextSettings uint32_t frame_buffers_count = 3U; bool vsync_enabled = true; bool is_full_screen = false; - ContextOptionMask options_mask; + ContextOptionMask options_mask { ContextOption::DeferredProgramBindingsInitialization }; uint32_t unsync_max_fps = 1000U; // MacOS only RenderContextSettings& SetFrameSize(FrameSize&& new_frame_size) noexcept; @@ -85,11 +85,11 @@ struct IRenderContext [[nodiscard]] virtual bool ReadyToRender() const = 0; virtual void Resize(const FrameSize& frame_size) = 0; virtual void Present() = 0; - [[nodiscard]] virtual Platform::AppView GetAppView() const = 0; - [[nodiscard]] virtual const Settings& GetSettings() const noexcept = 0; - [[nodiscard]] virtual uint32_t GetFrameBufferIndex() const noexcept = 0; - [[nodiscard]] virtual uint32_t GetFrameIndex() const noexcept = 0; - [[nodiscard]] virtual const IFpsCounter& GetFpsCounter() const noexcept = 0; + [[nodiscard]] virtual Platform::AppView GetAppView() const = 0; + [[nodiscard]] virtual const Settings& GetSettings() const noexcept = 0; + [[nodiscard]] virtual uint32_t GetFrameBufferIndex() const noexcept = 0; + [[nodiscard]] virtual uint32_t GetFrameIndex() const noexcept = 0; + [[nodiscard]] virtual const Data::IFpsCounter& GetFpsCounter() const noexcept = 0; virtual bool SetVSyncEnabled(bool vsync_enabled) = 0; virtual bool SetFrameBuffersCount(uint32_t frame_buffers_count) = 0; diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IRenderState.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IRenderState.h index 49f63054c..93fff2770 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IRenderState.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IRenderState.h @@ -24,7 +24,6 @@ Methane render state interface: specifies configuration of the graphics pipeline #pragma once #include "IObject.h" -#include "IProgram.h" #include #include diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IResource.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IResource.h index 7d6469a0d..716cc21a1 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IResource.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IResource.h @@ -95,13 +95,8 @@ struct IResource virtual bool SetState(State state, Ptr& out_barriers) = 0; virtual bool SetOwnerQueueFamily(uint32_t family_index) = 0; virtual bool SetOwnerQueueFamily(uint32_t family_index, Ptr& out_barriers) = 0; - virtual void SetData(const SubResources& sub_resources, ICommandQueue& target_cmd_queue) = 0; virtual void RestoreDescriptorViews(const DescriptorByViewId& descriptor_by_view_id) = 0; - - [[nodiscard]] virtual SubResource GetData(const SubResource::Index& sub_resource_index = SubResource::Index(), const BytesRangeOpt& data_range = {}) = 0; [[nodiscard]] virtual Data::Size GetDataSize(Data::MemoryState size_type = Data::MemoryState::Reserved) const noexcept = 0; - [[nodiscard]] virtual Data::Size GetSubResourceDataSize(const SubResource::Index& sub_resource_index = SubResource::Index()) const = 0; - [[nodiscard]] virtual const SubResource::Count& GetSubresourceCount() const noexcept = 0; [[nodiscard]] virtual Type GetResourceType() const noexcept = 0; [[nodiscard]] virtual State GetState() const noexcept = 0; [[nodiscard]] virtual UsageMask GetUsage() const noexcept = 0; diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ISampler.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ISampler.h index c646981a3..a593d5902 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ISampler.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ISampler.h @@ -53,6 +53,9 @@ struct SamplerFilter SamplerFilter(MinMag min_mag, Mip mip) : SamplerFilter(min_mag, min_mag, mip) { } explicit SamplerFilter(MinMag min_mag) : SamplerFilter(min_mag, Mip::NotMipmapped) { } + bool operator==(const SamplerFilter& other) const; + bool operator!=(const SamplerFilter& other) const; + MinMag min = MinMag::Nearest; MinMag mag = MinMag::Nearest; Mip mip = Mip::NotMipmapped; @@ -72,6 +75,9 @@ struct SamplerAddress SamplerAddress(Mode s, Mode t, Mode r) : s(s), t(t), r(r) { } explicit SamplerAddress(Mode all) : s(all), t(all), r(all) { } + bool operator==(const SamplerAddress& other) const; + bool operator!=(const SamplerAddress& other) const; + Mode s = Mode::ClampToEdge; // width Mode t = Mode::ClampToEdge; // height Mode r = Mode::ClampToEdge; // depth @@ -81,6 +87,9 @@ struct SamplerLevelOfDetail { SamplerLevelOfDetail(float bias = 0.F, float min = 0.F, float max = std::numeric_limits::max()); + bool operator==(const SamplerLevelOfDetail& other) const; + bool operator!=(const SamplerLevelOfDetail& other) const; + float min = 0.F; float max = std::numeric_limits::max(); float bias = 0.F; @@ -102,6 +111,9 @@ struct SamplerSettings SamplerBorderColor border_color = SamplerBorderColor::TransparentBlack, Compare compare_function = Compare::Never); + bool operator==(const SamplerSettings& other) const; + bool operator!=(const SamplerSettings& other) const; + SamplerFilter filter; SamplerAddress address; SamplerLevelOfDetail lod; diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IShader.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IShader.h index 2b1c5898e..41e8cf1d2 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IShader.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/IShader.h @@ -42,6 +42,7 @@ enum class ShaderType : uint32_t { Vertex, Pixel, + Compute, All }; @@ -52,14 +53,14 @@ struct ShaderMacroDefinition std::string name; std::string value; - explicit ShaderMacroDefinition(std::string name) - : name(std::move(name)) - { } + explicit ShaderMacroDefinition(std::string name); + ShaderMacroDefinition(std::string name, std::string value); - ShaderMacroDefinition(std::string name, std::string value) - : name(std::move(name)) - , value(std::move(value)) - { } + bool operator==(const ShaderMacroDefinition& other) const noexcept; + bool operator!=(const ShaderMacroDefinition& other) const noexcept; + + [[nodiscard]] static std::string ToString(const std::vector& macro_definitions, + std::string_view splitter = ", ") noexcept; }; using ShaderMacroDefinitions = std::vector; @@ -68,6 +69,9 @@ struct ShaderEntryFunction { std::string file_name; std::string function_name; + + bool operator==(const ShaderEntryFunction& other) const noexcept; + bool operator!=(const ShaderEntryFunction& other) const noexcept; }; struct ShaderSettings @@ -79,6 +83,9 @@ struct ShaderSettings // Optional parameters (by default shaders are precompiled to application resources and loaded through Data::IProvider) std::string source_file_path; std::string source_compile_target; + + bool operator==(const ShaderSettings& other) const noexcept; + bool operator!=(const ShaderSettings& other) const noexcept; }; struct IContext; @@ -95,9 +102,6 @@ struct IShader // Create IShader instance [[nodiscard]] static Ptr Create(Type type, const IContext& context, const Settings& settings); - // Auxiliary functions - [[nodiscard]] static std::string ConvertMacroDefinitionsToString(const MacroDefinitions& macro_definitions, std::string_view splitter = ", ") noexcept; - // IShader interface [[nodiscard]] virtual Ptr GetPtr() = 0; [[nodiscard]] virtual Type GetType() const noexcept = 0; diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ITexture.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ITexture.h index ea274cc35..a82635a3d 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ITexture.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ITexture.h @@ -82,6 +82,9 @@ struct TextureSettings Opt frame_index_opt; // for TextureType::FrameBuffer Opt depth_stencil_clear_opt; // for TextureType::DepthStencil + bool operator==(const TextureSettings& other) const; + bool operator!=(const TextureSettings& other) const; + [[nodiscard]] static TextureSettings ForImage(const Dimensions& dimensions, const Opt& array_length_opt, PixelFormat pixel_format, bool mipmapped, ResourceUsageMask usage = { ResourceUsage::ShaderRead }); [[nodiscard]] static TextureSettings ForCubeImage(uint32_t dimension_size, const Opt& array_length_opt, PixelFormat pixel_format, bool mipmapped, @@ -109,7 +112,13 @@ struct ITexture [[nodiscard]] static Ptr Create(const IContext& context, const Settings& settings); // ITexture interface - [[nodiscard]] virtual const Settings& GetSettings() const = 0; + [[nodiscard]] virtual const Settings& GetSettings() const = 0; + [[nodiscard]] virtual Data::Size GetSubResourceDataSize(const SubResource::Index& sub_resource_index = {}) const = 0; + [[nodiscard]] virtual SubResource::Count GetSubresourceCount() const noexcept = 0; + [[nodiscard]] virtual SubResource GetData(ICommandQueue& target_cmd_queue, + const SubResourceIndex& sub_resource_index = {}, + const BytesRangeOpt& data_range = {}) = 0; + virtual void SetData(ICommandQueue& target_cmd_queue, const SubResources& sub_resources) = 0; }; } // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/Interfaces.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/Interfaces.h index d9cf091b9..08805b7f0 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/Interfaces.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/Interfaces.h @@ -44,5 +44,6 @@ Methane graphics RHI interfaces: all headers under one umbrella. #include "ICommandListDebugGroup.h" #include "ICommandQueue.h" #include "ITransferCommandList.h" +#include "IComputeCommandList.h" #include "IRenderCommandList.h" #include "IParallelRenderCommandList.h" diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ResourceView.h b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ResourceView.h index c6d54b033..06dafdee0 100644 --- a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ResourceView.h +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/ResourceView.h @@ -223,6 +223,7 @@ class ResourceView [[nodiscard]] const SubResource::Index& GetSubresourceIndex() const noexcept { return m_settings.subresource_index; } [[nodiscard]] const SubResource::Count& GetSubresourceCount() const noexcept { return m_settings.subresource_count; } [[nodiscard]] Data::Size GetOffset() const noexcept { return m_settings.offset; } + [[nodiscard]] Data::Size GetSize() const noexcept { return m_settings.size; } [[nodiscard]] TextureDimensionType GetTextureDimensionType() const; private: @@ -239,10 +240,10 @@ static ResourceViews CreateResourceViews(const Ptrs& resource_ptr ResourceViews resource_views; std::transform(resource_ptrs.begin(), resource_ptrs.end(), std::back_inserter(resource_views), [](const Ptr& resource_ptr) - { - META_CHECK_ARG_NOT_NULL(resource_ptr); - return ResourceView(*resource_ptr); - }); + { + META_CHECK_ARG_NOT_NULL(resource_ptr); + return ResourceView(*resource_ptr); + }); return resource_views; } @@ -261,7 +262,7 @@ static ResourceViews CreateResourceViews(const std::vector& resour ResourceViews resource_views; std::transform(resources.begin(), resources.end(), std::back_inserter(resource_views), [](const ResourceType& resource) - { return ResourceView(resource.GetInterface()); }); + { return ResourceView(resource.GetInterface()); }); return resource_views; } diff --git a/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/TypeFormatters.hpp b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/TypeFormatters.hpp new file mode 100644 index 000000000..653f0285a --- /dev/null +++ b/Modules/Graphics/RHI/Interface/Include/Methane/Graphics/RHI/TypeFormatters.hpp @@ -0,0 +1,42 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/TypeFormatters.hpp +Methane Graphics RHI Type formatters for use with fmt::format(...) + +******************************************************************************/ + +#pragma once + +#include "ResourceView.h" + +template<> +struct fmt::formatter +{ + template + auto format(const Methane::Graphics::Rhi::SubResource::Index& index, FormatContext& ctx) { return format_to(ctx.out(), "{}", static_cast(index)); } + [[nodiscard]] constexpr auto parse(const format_parse_context& ctx) const { return ctx.end(); } +}; + +template<> +struct fmt::formatter +{ + template + auto format(const Methane::Graphics::Rhi::SubResource::Count& count, FormatContext& ctx) { return format_to(ctx.out(), "{}", static_cast(count)); } + [[nodiscard]] constexpr auto parse(const format_parse_context& ctx) const { return ctx.end(); } +}; \ No newline at end of file diff --git a/Modules/Graphics/RHI/Interface/README.md b/Modules/Graphics/RHI/Interface/README.md index 2045e8f1f..bf5f151d2 100644 --- a/Modules/Graphics/RHI/Interface/README.md +++ b/Modules/Graphics/RHI/Interface/README.md @@ -13,6 +13,7 @@ - [IRenderCommandList](Include/Methane/Graphics/IRenderCommandList.h) - [IParallelRenderCommandList](Include/Methane/Graphics/IParallelRenderCommandList.h) - [ITransferCommandList](Include/Methane/Graphics/ITransferCommandList.h) (work in progress) +- [IComputeCommandList](Include/Methane/Graphics/IComputeCommandList.h) (work in progress) - [IFence](Include/Methane/Graphics/IFence.h) - [ICommandKit](Include/Methane/Graphics/ICommandKit.h) diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IBuffer.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IBuffer.cpp index f021b9d15..ed9fb6809 100644 --- a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IBuffer.cpp +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IBuffer.cpp @@ -93,6 +93,18 @@ BufferSettings BufferSettings::ForReadBackBuffer(Data::Size size) }; } +bool BufferSettings::operator==(const BufferSettings& other) const +{ + return std::tie(type, usage_mask, size, item_stride_size, data_format, storage_mode) + == std::tie(other.type, other.usage_mask, other.size, other.item_stride_size, other.data_format, other.storage_mode); +} + +bool BufferSettings::operator!=(const BufferSettings& other) const +{ + return std::tie(type, usage_mask, size, item_stride_size, data_format, storage_mode) + != std::tie(other.type, other.usage_mask, other.size, other.item_stride_size, other.data_format, other.storage_mode); +} + Data::Size BufferSettings::GetAlignedSize(Data::Size size) noexcept { // Aligned size must be a multiple 256 bytes diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IComputeCommandList.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IComputeCommandList.cpp new file mode 100644 index 000000000..7305a6112 --- /dev/null +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IComputeCommandList.cpp @@ -0,0 +1,38 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/IComputeCommandList.cpp +Methane compute command list interface. + +******************************************************************************/ + +#include +#include + +#include + +namespace Methane::Graphics::Rhi +{ + +Ptr IComputeCommandList::Create(ICommandQueue& command_queue) +{ + META_FUNCTION_TASK(); + return command_queue.CreateComputeCommandList(); +} + +} // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IComputeContext.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IComputeContext.cpp new file mode 100644 index 000000000..3efcb2dde --- /dev/null +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IComputeContext.cpp @@ -0,0 +1,54 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/IComputeContext.cpp +Methane render context interface: represents graphics device and swap chain, +provides basic multi-frame rendering synchronization and frame presenting APIs. + +******************************************************************************/ + +#include +#include +#include + +#include + +namespace Methane::Graphics::Rhi +{ + +bool ComputeContextSettings::operator==(const ComputeContextSettings& other) const noexcept +{ + return options == other.options; +} +bool ComputeContextSettings::operator!=(const ComputeContextSettings& other) const noexcept +{ + return options != other.options; +} + +Ptr IComputeContext::Create(IDevice& device, tf::Executor& parallel_executor, const Settings& settings) +{ + META_FUNCTION_TASK(); + return device.CreateComputeContext(parallel_executor, settings); +} + +ICommandKit& IComputeContext::GetComputeCommandKit() const +{ + return GetDefaultCommandKit(CommandListType::Compute); +} + +} // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IComputeState.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IComputeState.cpp new file mode 100644 index 000000000..887a7a434 --- /dev/null +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IComputeState.cpp @@ -0,0 +1,65 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/RHI/IComputeState.cpp +Methane render state interface: specifies configuration of the graphics pipeline. + +******************************************************************************/ + +#include +#include +#include +#include + +#include + +#include + +namespace Methane::Graphics::Rhi +{ + +bool ComputeStateSettings::operator==(const ComputeStateSettings& other) const noexcept +{ + META_FUNCTION_TASK(); + return std::tie(program_ptr) == + std::tie(other.program_ptr); +} + +bool ComputeStateSettings::operator!=(const ComputeStateSettings& other) const noexcept +{ + META_FUNCTION_TASK(); + return std::tie(program_ptr) != + std::tie(other.program_ptr); +} + +ComputeStateSettings::operator std::string() const +{ + META_FUNCTION_TASK(); + return fmt::format(" - Program '{}';\n" + " - Thread Group Size: {}.", + program_ptr->GetName(), + thread_group_size); +} + +Ptr IComputeState::Create(const IContext& context, const Settings& state_settings) +{ + META_FUNCTION_TASK(); + return context.CreateComputeState(state_settings); +} + +} // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IDevice.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IDevice.cpp index 943825bd2..db24d01dc 100644 --- a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IDevice.cpp +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IDevice.cpp @@ -49,4 +49,11 @@ DeviceCaps& DeviceCaps::SetTransferQueuesCount(uint32_t new_transfer_queues_coun return *this; } +DeviceCaps& DeviceCaps::SetComputeQueuesCount(uint32_t new_compute_queues_count) noexcept +{ + META_FUNCTION_TASK(); + compute_queues_count = new_compute_queues_count; + return *this; +} + } // namespace Methane::Graphics::Rhi diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IProgram.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IProgram.cpp index b480a5de4..432ea6893 100644 --- a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IProgram.cpp +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IProgram.cpp @@ -47,6 +47,13 @@ bool ProgramArgument::operator==(const ProgramArgument& other) const noexcept std::tie(other.m_hash, other.m_shader_type, other.m_name); } +bool ProgramArgument::operator<(const ProgramArgument& other) const noexcept +{ + META_FUNCTION_TASK(); + return std::tie(m_hash, m_shader_type, m_name) < + std::tie(other.m_hash, other.m_shader_type, other.m_name); +} + ProgramArgument::operator std::string() const noexcept { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IRenderState.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IRenderState.cpp index caac51417..3a3940547 100644 --- a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IRenderState.cpp +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IRenderState.cpp @@ -23,6 +23,7 @@ Methane render state interface: specifies configuration of the graphics pipeline #include #include +#include #include #include diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ISampler.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ISampler.cpp index 3a8cc18e6..a3e271e94 100644 --- a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ISampler.cpp +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ISampler.cpp @@ -29,6 +29,30 @@ Methane sampler interface: GPU resource for texture sampling. namespace Methane::Graphics::Rhi { +bool SamplerFilter::operator==(const SamplerFilter& other) const +{ + return std::tie(min, mag, mip) + == std::tie(other.min, other.mag, other.mip); +} + +bool SamplerFilter::operator!=(const SamplerFilter& other) const +{ + return std::tie(min, mag, mip) + != std::tie(other.min, other.mag, other.mip); +} + +bool SamplerAddress::operator==(const SamplerAddress& other) const +{ + return std::tie(s, t, r) + == std::tie(other.s, other.t, other.r); +} + +bool SamplerAddress::operator!=(const SamplerAddress& other) const +{ + return std::tie(s, t, r) + != std::tie(other.s, other.t, other.r); +} + SamplerSettings::SamplerSettings(const SamplerFilter& filter, const SamplerAddress& address, const SamplerLevelOfDetail& lod, uint32_t max_anisotropy, SamplerBorderColor border_color, Compare compare_function) @@ -40,12 +64,36 @@ SamplerSettings::SamplerSettings(const SamplerFilter& filter, const SamplerAddre , compare_function(compare_function) { } +bool SamplerSettings::operator==(const SamplerSettings& other) const +{ + return std::tie(filter, address, lod, max_anisotropy, border_color, compare_function) + == std::tie(other.filter, other.address, other.lod, other.max_anisotropy, other.border_color, other.compare_function); +} + +bool SamplerSettings::operator!=(const SamplerSettings& other) const +{ + return std::tie(filter, address, lod, max_anisotropy, border_color, compare_function) + != std::tie(other.filter, other.address, other.lod, other.max_anisotropy, other.border_color, other.compare_function); +} + SamplerLevelOfDetail::SamplerLevelOfDetail(float bias, float min, float max) : min(min) , max(max) , bias(bias) { } +bool SamplerLevelOfDetail::operator==(const SamplerLevelOfDetail& other) const +{ + return std::tie(min, max, bias) + == std::tie(other.min, other.max, other.bias); +} + +bool SamplerLevelOfDetail::operator!=(const SamplerLevelOfDetail& other) const +{ + return std::tie(min, max, bias) + != std::tie(other.min, other.max, other.bias); +} + Ptr ISampler::Create(const IContext& context, const Settings& settings) { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IShader.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IShader.cpp index 0c1ac10b7..a5e7ca3ae 100644 --- a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IShader.cpp +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/IShader.cpp @@ -31,23 +31,70 @@ Methane shader interface: defines programmable stage of the graphics pipeline. namespace Methane::Graphics::Rhi { +ShaderMacroDefinition::ShaderMacroDefinition(std::string name) + : name(std::move(name)) +{ } + +ShaderMacroDefinition::ShaderMacroDefinition(std::string name, std::string value) + : name(std::move(name)) + , value(std::move(value)) +{ } + +bool ShaderMacroDefinition::operator==(const ShaderMacroDefinition& other) const noexcept +{ + return std::tie(name, value) == std::tie(other.name, other.value); +} + +bool ShaderMacroDefinition::operator!=(const ShaderMacroDefinition& other) const noexcept +{ + return std::tie(name, value) != std::tie(other.name, other.value); +} + +bool ShaderEntryFunction::operator==(const ShaderEntryFunction& other) const noexcept +{ + return std::tie(file_name, function_name) == std::tie(other.file_name, other.function_name); +} + +bool ShaderEntryFunction::operator!=(const ShaderEntryFunction& other) const noexcept +{ + return std::tie(file_name, function_name) != std::tie(other.file_name, other.function_name); +} + +bool ShaderSettings::operator==(const ShaderSettings& other) const noexcept +{ + if (std::addressof(data_provider) != std::addressof(other.data_provider)) + return false; + + return std::tie(entry_function, compile_definitions, source_file_path, source_compile_target) + == std::tie(other.entry_function, other.compile_definitions, other.source_file_path, other.source_compile_target); +} + +bool ShaderSettings::operator!=(const ShaderSettings& other) const noexcept +{ + if (std::addressof(data_provider) == std::addressof(other.data_provider)) + return false; + + return std::tie(entry_function, compile_definitions, source_file_path, source_compile_target) + != std::tie(other.entry_function, other.compile_definitions, other.source_file_path, other.source_compile_target); +} + Ptr IShader::Create(Type type, const IContext& context, const Settings& settings) { return context.CreateShader(type, settings); } -std::string IShader::ConvertMacroDefinitionsToString(const MacroDefinitions& macro_definitions, std::string_view splitter) noexcept +std::string ShaderMacroDefinition::ToString(const std::vector& macro_definitions, std::string_view splitter) noexcept { META_FUNCTION_TASK(); std::stringstream ss; - bool is_first_defintion = true; - for(const MacroDefinition& macro_definition : macro_definitions) + bool is_first_definition = true; + for(const ShaderMacroDefinition& macro_definition : macro_definitions) { - if (!is_first_defintion) + if (!is_first_definition) ss << splitter; ss << macro_definition.name << "=" << macro_definition.value; - is_first_defintion = false; + is_first_definition = false; } return ss.str(); } diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ITexture.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ITexture.cpp index 5f13f7b3a..857b736e0 100644 --- a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ITexture.cpp +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ITexture.cpp @@ -41,6 +41,19 @@ Rhi::ITexture& TextureView::GetTexture() const return *m_texture_ptr; } +bool TextureSettings::operator==(const TextureSettings& other) const +{ + return std::tie(type, dimension_type, usage_mask, pixel_format, dimensions, array_length, mipmapped, frame_index_opt, depth_stencil_clear_opt) + == std::tie(other.type, other.dimension_type, other.usage_mask, other.pixel_format, other.dimensions, other.array_length, other.mipmapped, + other.frame_index_opt, other.depth_stencil_clear_opt); +} +bool TextureSettings::operator!=(const TextureSettings& other) const +{ + return std::tie(type, dimension_type, usage_mask, pixel_format, dimensions, array_length, mipmapped, frame_index_opt, depth_stencil_clear_opt) + != std::tie(other.type, other.dimension_type, other.usage_mask, other.pixel_format, other.dimensions, other.array_length, other.mipmapped, + other.frame_index_opt, other.depth_stencil_clear_opt); +} + TextureSettings TextureSettings::ForImage(const Dimensions& dimensions, const Opt& array_length_opt, PixelFormat pixel_format, bool mipmapped, ResourceUsageMask usage) { diff --git a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ResourceView.cpp b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ResourceView.cpp index f5195f984..77ee3ab5a 100644 --- a/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ResourceView.cpp +++ b/Modules/Graphics/RHI/Interface/Sources/Methane/Graphics/RHI/ResourceView.cpp @@ -21,6 +21,7 @@ Methane sub-resource used for resource data transfers. ******************************************************************************/ +#include "Methane/Graphics/RHI/IContext.h" #include #include #include @@ -33,6 +34,16 @@ Methane sub-resource used for resource data transfers. namespace Methane::Graphics::Rhi { +static SubResourceCount GetSubresourceCount(const IResource& resource) +{ + switch(resource.GetResourceType()) + { + case ResourceType::Texture: return dynamic_cast(resource).GetSubresourceCount(); + case ResourceType::Buffer: return SubResourceCount(1U, 1U, 1U); + default: return SubResourceCount(0U, 0U, 0U); + } +} + SubResource::SubResource(Data::Bytes&& data, const Index& index, BytesRangeOpt data_range) noexcept : Data::Chunk(std::move(data)) , m_index(index) @@ -51,9 +62,6 @@ SubResourceCount::SubResourceCount(Data::Size depth, Data::Size array_size, Data , m_mip_levels_count(mip_levels_count) { META_FUNCTION_TASK(); - META_CHECK_ARG_NOT_ZERO_DESCR(depth, "subresource count can not be zero"); - META_CHECK_ARG_NOT_ZERO_DESCR(array_size, "subresource count can not be zero"); - META_CHECK_ARG_NOT_ZERO_DESCR(mip_levels_count, "subresource count can not be zero"); } void SubResourceCount::operator+=(const SubResourceIndex& other) noexcept @@ -212,7 +220,7 @@ ResourceView::ResourceView(IResource& resource, const Settings& settings) { } ResourceView::ResourceView(IResource& resource, Data::Size offset, Data::Size size) - : ResourceView(resource, SubResourceIndex(), resource.GetSubresourceCount(), offset, size) + : ResourceView(resource, SubResourceIndex(), Rhi::GetSubresourceCount(resource), offset, size) { } ResourceView::ResourceView(IResource& resource, @@ -261,7 +269,7 @@ ResourceView::operator std::string() const if (!m_resource_ptr) return "Null resource view_id"; - return fmt::format("{} '{}' subresources from {} count {} with offset {}", + return fmt::format("{} '{}' subresources from {} for {} with offset {}", magic_enum::enum_name(m_resource_ptr->GetResourceType()), m_resource_ptr->GetName(), static_cast(m_settings.subresource_index), diff --git a/Modules/Graphics/RHI/Metal/CMakeLists.txt b/Modules/Graphics/RHI/Metal/CMakeLists.txt index 780089091..c42d2a625 100644 --- a/Modules/Graphics/RHI/Metal/CMakeLists.txt +++ b/Modules/Graphics/RHI/Metal/CMakeLists.txt @@ -12,6 +12,7 @@ list(APPEND HEADERS ${INCLUDE_DIR}/IContext.h ${INCLUDE_DIR}/Context.hpp ${INCLUDE_DIR}/RenderContext.hh + ${INCLUDE_DIR}/ComputeContext.hh ${INCLUDE_DIR}/RenderContextAppView.hh ${INCLUDE_DIR}/Shader.hh ${INCLUDE_DIR}/Program.hh @@ -20,6 +21,7 @@ list(APPEND HEADERS ${INCLUDE_DIR}/ProgramBindings.hh ${INCLUDE_DIR}/RenderState.hh ${INCLUDE_DIR}/ViewState.hh + ${INCLUDE_DIR}/ComputeState.hh ${INCLUDE_DIR}/Resource.hh ${INCLUDE_DIR}/ResourceBarriers.hh ${INCLUDE_DIR}/DescriptorManager.h @@ -35,6 +37,7 @@ list(APPEND HEADERS ${INCLUDE_DIR}/CommandListDebugGroup.hh ${INCLUDE_DIR}/CommandList.hpp ${INCLUDE_DIR}/TransferCommandList.hh + ${INCLUDE_DIR}/ComputeCommandList.hh ${INCLUDE_DIR}/RenderCommandList.hh ${INCLUDE_DIR}/ParallelRenderCommandList.hh ) @@ -52,6 +55,7 @@ list(APPEND SOURCES ${SOURCES_DIR}/ProgramBindings.mm ${SOURCES_DIR}/RenderState.mm ${SOURCES_DIR}/ViewState.mm + ${SOURCES_DIR}/ComputeState.mm ${SOURCES_DIR}/Resource.mm ${SOURCES_DIR}/Buffer.mm ${SOURCES_DIR}/BufferSet.mm @@ -64,6 +68,7 @@ list(APPEND SOURCES ${SOURCES_DIR}/CommandListSet.mm ${SOURCES_DIR}/CommandListDebugGroup.mm ${SOURCES_DIR}/TransferCommandList.mm + ${SOURCES_DIR}/ComputeCommandList.mm ${SOURCES_DIR}/RenderCommandList.mm ${SOURCES_DIR}/ParallelRenderCommandList.mm ) diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Buffer.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Buffer.hh index 3b0af1a9d..20fad182b 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Buffer.hh +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Buffer.hh @@ -39,7 +39,8 @@ public: Buffer(const Base::Context& context, const Settings& settings); // IResource interface - void SetData(const SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) override; + void SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResource& sub_resource) override; + SubResource GetData(Rhi::ICommandQueue& target_cmd_queue, const BytesRangeOpt& data_range = {}) override; // IObject interface bool SetName(std::string_view name) override; @@ -48,8 +49,10 @@ public: MTLIndexType GetNativeIndexType() const noexcept; private: - void SetDataToManagedBuffer(const SubResources& sub_resources); - void SetDataToPrivateBuffer(const SubResources& sub_resources); + void SetDataToManagedBuffer(const SubResource& sub_resource); + void SetDataToPrivateBuffer(const SubResource& sub_resource); + Data::Bytes GetDataFromManagedBuffer(const BytesRange& data_range); + Data::Bytes GetDataFromPrivateBuffer(const BytesRange& data_range); id m_mtl_buffer; }; diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/CommandList.hpp b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/CommandList.hpp index 91d0e5e43..a0505c6a7 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/CommandList.hpp +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/CommandList.hpp @@ -164,7 +164,7 @@ class CommandList : public CommandListBaseT } const MTLCommandEncoderId& GetNativeCommandEncoder() const noexcept { return m_mtl_cmd_encoder; } - const id& GetNativeCommandBuffer() const noexcept { return m_mtl_cmd_buffer; } + const id& GetNativeCommandBuffer() const noexcept { return m_mtl_cmd_buffer; } CommandQueue& GetMetalCommandQueue() noexcept { diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/CommandQueue.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/CommandQueue.hh index 316869fb6..e0d46399b 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/CommandQueue.hh +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/CommandQueue.hh @@ -43,6 +43,7 @@ public: // ICommandQueue interface [[nodiscard]] Ptr CreateFence() override; [[nodiscard]] Ptr CreateTransferCommandList() override; + [[nodiscard]] Ptr CreateComputeCommandList() override; [[nodiscard]] Ptr CreateRenderCommandList(Rhi::IRenderPass& render_pass) override; [[nodiscard]] Ptr CreateParallelRenderCommandList(Rhi::IRenderPass& render_pass) override; [[nodiscard]] Ptr CreateTimestampQueryPool(uint32_t max_timestamps_per_frame) override; diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ComputeCommandList.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ComputeCommandList.hh new file mode 100644 index 000000000..b5376e8c8 --- /dev/null +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ComputeCommandList.hh @@ -0,0 +1,50 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Metal/ComputeCommandList.hh +Metal implementation of the compute command list interface. + +******************************************************************************/ + +#pragma once + +#include "CommandList.hpp" + +#include + +#import + +namespace Methane::Graphics::Metal +{ + +class CommandQueue; + +class ComputeCommandList final // NOSONAR - inheritance hierarchy depth is greater than 5 + : public CommandList, Base::ComputeCommandList> +{ +public: + ComputeCommandList(Base::CommandQueue& command_queue); + + // ICommandList interface + void Reset(Rhi::ICommandListDebugGroup* debug_group_ptr = nullptr) override; + + // IComputeCommandList interface + void Dispatch(const Rhi::ThreadGroupsCount& thread_groups_count) override; +}; + +} // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ComputeContext.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ComputeContext.hh new file mode 100644 index 000000000..bb5bfd82b --- /dev/null +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ComputeContext.hh @@ -0,0 +1,40 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Metal/ComputeContext.hh +Metal implementation of the compute context interface. + +******************************************************************************/ + +#pragma once + +#include "Context.hpp" + +#include + +namespace Methane::Graphics::Metal +{ + +class ComputeContext final // NOSONAR - inheritance hierarchy depth is greater than 5 + : public Context +{ +public: + using Context::Context; +}; + +} // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ComputeState.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ComputeState.hh new file mode 100644 index 000000000..ecce730cd --- /dev/null +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ComputeState.hh @@ -0,0 +1,64 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Metal/ComputeState.hh +Metal implementation of the render state interface. + +******************************************************************************/ + +#pragma once + +#include + +#import + +#include + +namespace Methane::Graphics::Metal +{ + +class Device; + +class ComputeState final + : public Base::ComputeState +{ +public: + ComputeState(const Rhi::IContext& context, const Settings& settings); + + // IComputeState interface + void Reset(const Settings& settings) override; + + // Base::ComputeState interface + void Apply(Base::ComputeCommandList& command_list) override; + + // IObject interface + bool SetName(std::string_view name) override; + + void InitializeNativePipelineState(); + + id GetNativePipelineState(); + +private: + void ResetNativeState(); + + const Device& m_device; + MTLComputePipelineDescriptor* m_mtl_pipeline_state_desc = nil; + id m_mtl_pipeline_state = nil; +}; + +} // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Context.hpp b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Context.hpp index 935ff6563..b399e66ba 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Context.hpp +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Context.hpp @@ -25,6 +25,13 @@ Metal template implementation of the base context interface. #include "IContext.h" #include "Device.hh" +#include "CommandQueue.hh" +#include "Shader.hh" +#include "Program.hh" +#include "ComputeState.hh" +#include "Buffer.hh" +#include "Texture.hh" +#include "Sampler.hh" #include "ProgramLibrary.hh" #include "DescriptorManager.h" @@ -52,6 +59,48 @@ class Context // IContext overrides + [[nodiscard]] Ptr CreateCommandQueue(Rhi::CommandListType type) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, type); + } + + [[nodiscard]] Ptr CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(type, *this, settings); + } + + [[nodiscard]] Ptr CreateProgram(const Rhi::ProgramSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateComputeState(const Rhi::ComputeStateSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateBuffer(const Rhi::BufferSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateTexture(const Rhi::TextureSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateSampler(const Rhi::SamplerSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + const Device& GetMetalDevice() const noexcept final { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Device.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Device.hh index 55be2ff6f..a8dbb82af 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Device.hh +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Device.hh @@ -39,7 +39,8 @@ public: Device(const id& mtl_device, const Capabilities& capabilities); // IDevice interface - [[nodiscard]] Ptr CreateRenderContext(const Platform::AppEnvironment& env, tf::Executor& parallel_executor, const Rhi::RenderContextSettings& settings) override; + [[nodiscard]] Ptr CreateRenderContext(const Platform::AppEnvironment& env, tf::Executor& parallel_executor, const Rhi::RenderContextSettings& settings) override; + [[nodiscard]] Ptr CreateComputeContext(tf::Executor& parallel_executor, const Rhi::ComputeContextSettings& settings) override; const id& GetNativeDevice() const { return m_mtl_device; } diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Program.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Program.hh index 36c2475bb..0892598f8 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Program.hh +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Program.hh @@ -33,7 +33,8 @@ namespace Methane::Graphics::Metal struct IContext; class Shader; -class Program final : public Base::Program +class Program final + : public Base::Program { public: Program(const Base::Context& context, const Settings& settings); @@ -48,10 +49,11 @@ public: private: const IContext& GetMetalContext() const noexcept; - void SetNativeShaderArguments(Rhi::ShaderType shader_type, NSArray* mtl_arguments) noexcept; + void ReflectRenderPipelineArguments(); + void ReflectComputePipelineArguments(); + void SetNativeShaderArguments(Rhi::ShaderType shader_type, NSArray>* mtl_arguments) noexcept; - MTLVertexDescriptor* m_mtl_vertex_desc = nil; - id m_mtl_dummy_pipeline_state_for_reflection; + MTLVertexDescriptor* m_mtl_vertex_desc = nil; }; } // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ProgramBindings.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ProgramBindings.hh index 042f60b01..63b4af6be 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ProgramBindings.hh +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/ProgramBindings.hh @@ -33,6 +33,8 @@ namespace Methane::Graphics::Metal { class Program; +class RenderCommandList; +class ComputeCommandList; class ProgramBindings final : public Base::ProgramBindings @@ -49,6 +51,13 @@ public: // Base::ProgramBindings interface void CompleteInitialization() override { } + +private: + template // function void(const ArgumentBinding&) + void ForEachChangedArgumentBinding(const Base::ProgramBindings* applied_program_bindings_ptr, ApplyBehaviorMask apply_behavior, FuncType functor) const; + + void Apply(RenderCommandList& argument_binding, ApplyBehaviorMask apply_behavior) const; + void Apply(ComputeCommandList& compute_command_list, ApplyBehaviorMask apply_behavior) const; }; } // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/RenderContext.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/RenderContext.hh index 3d29fd9cb..563fd040a 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/RenderContext.hh +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/RenderContext.hh @@ -42,12 +42,6 @@ public: ~RenderContext() override; // IContext interface - [[nodiscard]] Ptr CreateCommandQueue(Rhi::CommandListType type) const override; - [[nodiscard]] Ptr CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const override; - [[nodiscard]] Ptr CreateProgram(const Rhi::ProgramSettings& settings) const override; - [[nodiscard]] Ptr CreateBuffer(const Rhi::BufferSettings& settings) const override; - [[nodiscard]] Ptr CreateTexture(const Rhi::TextureSettings& settings) const override; - [[nodiscard]] Ptr CreateSampler(const Rhi::SamplerSettings& settings) const override; void WaitForGpu(WaitFor wait_for) override; // IRenderContext interface diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Resource.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Resource.hh index 51930c50e..e4388cf87 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Resource.hh +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Resource.hh @@ -23,6 +23,7 @@ Metal implementation of the resource interface. #pragma once #include "Device.hh" +#include "Methane/Graphics/RHI/ResourceView.h" #include #include @@ -66,10 +67,10 @@ protected: return dynamic_cast(Base::Resource::GetBaseContext()); } - id GetUploadSubresourceBuffer(const Rhi::IResource::SubResource& sub_resource) + id GetUploadSubresourceBuffer(const Rhi::SubResource& sub_resource, Rhi::SubResourceCount subresource_count) { META_FUNCTION_TASK(); - const Data::Index sub_resource_raw_index = sub_resource.GetIndex().GetRawIndex(Base::Resource::GetSubresourceCount()); + const Data::Index sub_resource_raw_index = sub_resource.GetIndex().GetRawIndex(subresource_count); m_upload_subresource_buffers.resize(sub_resource_raw_index + 1); id mtl_upload_subresource_buffer = m_upload_subresource_buffers[sub_resource_raw_index]; @@ -83,15 +84,28 @@ protected: } else { - Data::RawPtr p_resource_data = static_cast([mtl_upload_subresource_buffer contents]); - META_CHECK_ARG_NOT_NULL(p_resource_data); - std::copy(sub_resource.GetDataPtr(), sub_resource.GetDataEndPtr(), p_resource_data); + Data::RawPtr resource_data_ptr = static_cast([mtl_upload_subresource_buffer contents]); + META_CHECK_ARG_NOT_NULL(resource_data_ptr); + std::copy(sub_resource.GetDataPtr(), sub_resource.GetDataEndPtr(), resource_data_ptr); } return mtl_upload_subresource_buffer; } + id GetReadBackBuffer(Data::Size data_size) + { + META_FUNCTION_TASK(); + if (!m_mtl_read_back_buffer || m_mtl_read_back_buffer.length != data_size) + { + const id& mtl_device = GetMetalContext().GetMetalDevice().GetNativeDevice(); + m_mtl_read_back_buffer = [mtl_device newBufferWithLength:data_size + options:MTLResourceStorageModeShared]; + } + return m_mtl_read_back_buffer; + } + private: std::vector> m_upload_subresource_buffers; + id m_mtl_read_back_buffer; }; } // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Shader.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Shader.hh index bbd91682b..be697282e 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Shader.hh +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Shader.hh @@ -42,15 +42,15 @@ public: // Base::Shader interface Ptrs GetArgumentBindings(const Rhi::ProgramArgumentAccessors& argument_accessors) const final; - id GetNativeFunction() noexcept { return m_mtl_function; } + id GetNativeFunction() noexcept { return m_mtl_function; } MTLVertexDescriptor* GetNativeVertexDescriptor(const Program& program) const; - void SetNativeArguments(NSArray* mtl_arguments) noexcept { m_mtl_arguments = mtl_arguments; } + void SetNativeBindings(NSArray>* mtl_bindings) noexcept { m_mtl_bindings = mtl_bindings; } private: const IContext& GetMetalContext() const noexcept; - id m_mtl_function; - NSArray* m_mtl_arguments = nil; + id m_mtl_function; + NSArray>* m_mtl_bindings = nil; }; } // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Texture.hh b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Texture.hh index 3c2afd4bc..62b5bc3af 100644 --- a/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Texture.hh +++ b/Modules/Graphics/RHI/Metal/Include/Methane/Graphics/Metal/Texture.hh @@ -43,7 +43,10 @@ public: Texture(const Base::Context& context, const Settings& settings); // IResource interface - void SetData(const SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) override; + void SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResources& sub_resources) override; + SubResource GetData(Rhi::ICommandQueue& target_cmd_queue, + const SubResource::Index& sub_resource_index = SubResource::Index(), + const BytesRangeOpt& data_range = {}) override; // IObject interface bool SetName(std::string_view name) override; diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Buffer.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Buffer.mm index dd27b6e5f..023aa6ffb 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Buffer.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Buffer.mm @@ -66,45 +66,61 @@ static MTLResourceOptions GetNativeResourceOptions(Rhi::BufferStorageMode storag return true; } -void Buffer::SetData(const SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) +void Buffer::SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResource& sub_resource) { META_FUNCTION_TASK(); - Resource::SetData(sub_resources, target_cmd_queue); + Base::Buffer::SetData(target_cmd_queue, sub_resource); switch(GetSettings().storage_mode) { - case IBuffer::StorageMode::Managed: SetDataToManagedBuffer(sub_resources); break; - case IBuffer::StorageMode::Private: SetDataToPrivateBuffer(sub_resources); break; + case IBuffer::StorageMode::Managed: SetDataToManagedBuffer(sub_resource); break; + case IBuffer::StorageMode::Private: SetDataToPrivateBuffer(sub_resource); break; default: META_UNEXPECTED_ARG(GetSettings().storage_mode); } } -void Buffer::SetDataToManagedBuffer(const SubResources& sub_resources) +Rhi::SubResource Buffer::GetData(Rhi::ICommandQueue&, const BytesRangeOpt& data_range) +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_TRUE_DESCR(GetUsage().HasAnyBit(Rhi::ResourceUsage::ReadBack), + "getting buffer data from GPU is allowed for buffers with CPU Read-back flag only"); + + const BytesRange buffer_data_range(data_range ? data_range->GetStart() : 0U, + data_range ? data_range->GetEnd() : GetDataSize()); + + Data::Bytes data; + switch(GetSettings().storage_mode) + { + case IBuffer::StorageMode::Managed: data = GetDataFromManagedBuffer(buffer_data_range); break; + case IBuffer::StorageMode::Private: data = GetDataFromPrivateBuffer(buffer_data_range); break; + default: META_UNEXPECTED_ARG_RETURN(GetSettings().storage_mode, SubResource()); + } + + return Rhi::SubResource(std::move(data), Rhi::SubResourceIndex(), data_range); +} + +void Buffer::SetDataToManagedBuffer(const SubResource& sub_resource) { META_FUNCTION_TASK(); META_CHECK_ARG_EQUAL(GetSettings().storage_mode, IBuffer::StorageMode::Managed); META_CHECK_ARG_NOT_NULL(m_mtl_buffer); META_CHECK_ARG_EQUAL(m_mtl_buffer.storageMode, NativeStorageModeManaged); - Data::RawPtr p_resource_data = static_cast([m_mtl_buffer contents]); - META_CHECK_ARG_NOT_NULL(p_resource_data); + Data::RawPtr resource_data_ptr = static_cast([m_mtl_buffer contents]); + META_CHECK_ARG_NOT_NULL(resource_data_ptr); Data::Size data_offset = 0; - for(const SubResource& sub_resource : sub_resources) - { - if (sub_resource.HasDataRange()) - data_offset = sub_resource.GetDataRange().GetStart(); + if (sub_resource.HasDataRange()) + data_offset = sub_resource.GetDataRange().GetStart(); - std::copy(sub_resource.GetDataPtr(), sub_resource.GetDataEndPtr(), p_resource_data + data_offset); + std::copy(sub_resource.GetDataPtr(), sub_resource.GetDataEndPtr(), resource_data_ptr + data_offset); #ifdef APPLE_MACOS // storage_mode == MTLStorageModeManaged - [m_mtl_buffer didModifyRange:NSMakeRange(data_offset, data_offset + sub_resource.GetDataSize())]; + [m_mtl_buffer didModifyRange:NSMakeRange(data_offset, data_offset + sub_resource.GetDataSize())]; #endif - data_offset += sub_resource.GetDataSize(); - } } -void Buffer::SetDataToPrivateBuffer(const SubResources& sub_resources) +void Buffer::SetDataToPrivateBuffer(const SubResource& sub_resource) { META_FUNCTION_TASK(); META_CHECK_ARG_EQUAL(GetSettings().storage_mode, IBuffer::StorageMode::Private); @@ -118,23 +134,52 @@ static MTLResourceOptions GetNativeResourceOptions(Rhi::BufferStorageMode storag META_CHECK_ARG_NOT_NULL(mtl_blit_encoder); Data::Size data_offset = 0; - for(const SubResource& sub_resource : sub_resources) - { - if (sub_resource.HasDataRange()) - data_offset = sub_resource.GetDataRange().GetStart(); + if (sub_resource.HasDataRange()) + data_offset = sub_resource.GetDataRange().GetStart(); - [mtl_blit_encoder copyFromBuffer:GetUploadSubresourceBuffer(sub_resource) - sourceOffset:0 - toBuffer:m_mtl_buffer - destinationOffset:data_offset - size:sub_resource.GetDataSize()]; + [mtl_blit_encoder copyFromBuffer:GetUploadSubresourceBuffer(sub_resource, Rhi::SubResourceCount()) + sourceOffset:0U + toBuffer:m_mtl_buffer + destinationOffset:data_offset + size:sub_resource.GetDataSize()]; - data_offset += sub_resource.GetDataSize(); - } - GetBaseContext().RequestDeferredAction(Rhi::ContextDeferredAction::UploadResources); } +Data::Bytes Buffer::GetDataFromManagedBuffer(const BytesRange& data_range) +{ + META_FUNCTION_TASK(); + auto* data_ptr = reinterpret_cast([m_mtl_buffer contents]); + Data::Size data_size = static_cast([m_mtl_buffer length]); + + META_CHECK_ARG_LESS_DESCR(data_range.GetEnd(), data_size, "provided subresource data range is out of buffer bounds"); + data_ptr += data_range.GetStart(); + + return Data::Bytes(data_ptr, data_ptr + data_range.GetLength()); +} + +Data::Bytes Buffer::GetDataFromPrivateBuffer(const BytesRange& data_range) +{ + META_FUNCTION_TASK(); + TransferCommandList& transfer_command_list = dynamic_cast(GetBaseContext().GetUploadCommandKit().GetListForEncoding()); + transfer_command_list.RetainResource(*this); + + const id& mtl_blit_encoder = transfer_command_list.GetNativeCommandEncoder(); + META_CHECK_ARG_NOT_NULL(mtl_blit_encoder); + + const id mtl_read_back_buffer = GetReadBackBuffer(data_range.GetLength()); + [mtl_blit_encoder copyFromBuffer:m_mtl_buffer + sourceOffset:data_range.GetStart() + toBuffer:mtl_read_back_buffer + destinationOffset:0U + size:data_range.GetLength()]; + + GetBaseContext().UploadResources(); + + auto* data_ptr = reinterpret_cast([mtl_read_back_buffer contents]) + data_range.GetStart(); + return Data::Bytes(data_ptr, data_ptr + data_range.GetLength()); +} + MTLIndexType Buffer::GetNativeIndexType() const noexcept { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/CommandQueue.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/CommandQueue.mm index 59daa5c22..3edc10d9e 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/CommandQueue.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/CommandQueue.mm @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,12 @@ return std::make_shared(*this); } +Ptr CommandQueue::CreateComputeCommandList() +{ + META_FUNCTION_TASK(); + return std::make_shared(*this); +} + Ptr CommandQueue::CreateRenderCommandList(Rhi::IRenderPass& render_pass) { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ComputeCommandList.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ComputeCommandList.mm new file mode 100644 index 000000000..fb86a98b2 --- /dev/null +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ComputeCommandList.mm @@ -0,0 +1,66 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Metal/ComputeCommandList.mm +Metal implementation of the compute command list interface. + +******************************************************************************/ + +#include +#include + +#include +#include + +namespace Methane::Graphics::Metal +{ + +ComputeCommandList::ComputeCommandList(Base::CommandQueue& command_queue) + : CommandList(true, command_queue) +{ } + +void ComputeCommandList::Reset(Rhi::ICommandListDebugGroup* debug_group_ptr) +{ + META_FUNCTION_TASK(); + if (IsCommandEncoderInitialized()) + { + Base::CommandList::Reset(debug_group_ptr); + return; + } + + const id& mtl_cmd_buffer = InitializeCommandBuffer(); + InitializeCommandEncoder([mtl_cmd_buffer computeCommandEncoder]); + Base::CommandList::Reset(debug_group_ptr); +} + +void ComputeCommandList::Dispatch(const Rhi::ThreadGroupsCount& thread_groups_count) +{ + META_FUNCTION_TASK(); + Base::ComputeCommandList::Dispatch(thread_groups_count); + + const auto& mtl_cmd_encoder = GetNativeCommandEncoder(); + META_CHECK_ARG_NOT_NULL(mtl_cmd_encoder); + + const Rhi::ThreadGroupSize& thread_group_size = GetComputeState().GetSettings().thread_group_size; + const MTLSize mtl_thread_groups{ thread_groups_count.GetWidth(), thread_groups_count.GetHeight(), thread_groups_count.GetDepth() }; + const MTLSize mtl_threads_per_group{ thread_group_size.GetWidth(), thread_group_size.GetHeight(), thread_group_size.GetDepth() }; + [mtl_cmd_encoder dispatchThreadgroups: mtl_thread_groups + threadsPerThreadgroup: mtl_threads_per_group]; +} + +} // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ComputeState.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ComputeState.mm new file mode 100644 index 000000000..8f71e153b --- /dev/null +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ComputeState.mm @@ -0,0 +1,122 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Metal/ComputeState.mm +Metal implementation of the render state interface. + +******************************************************************************/ + +#include +#include +#include +#include + +#include +#include + +namespace Methane::Graphics::Metal +{ + +static const Device& GetMetalDeviceFromContext(const Rhi::IContext& context) +{ + META_FUNCTION_TASK(); + switch (context.GetType()) + { + case Rhi::ContextType::Render: + return dynamic_cast(context).GetMetalDevice(); + + case Rhi::ContextType::Compute: + return dynamic_cast(context).GetMetalDevice(); + } + return dynamic_cast(context).GetMetalDevice(); +} + +ComputeState::ComputeState(const Rhi::IContext& context, const Settings& settings) + : Base::ComputeState(context, settings) + , m_device(GetMetalDeviceFromContext(context)) +{ + META_FUNCTION_TASK(); + Reset(settings); +} + +void ComputeState::Reset(const Settings& settings) +{ + META_FUNCTION_TASK(); + Base::ComputeState::Reset(settings); + + Program& metal_program = static_cast(*settings.program_ptr); + m_mtl_pipeline_state_desc = [[MTLComputePipelineDescriptor alloc] init]; + m_mtl_pipeline_state_desc.computeFunction = metal_program.GetNativeShaderFunction(Rhi::ShaderType::Compute); + + ResetNativeState(); +} + +void ComputeState::Apply(Base::ComputeCommandList& command_list) +{ + META_FUNCTION_TASK(); + auto& metal_command_list = static_cast(command_list); + const id& mtl_cmd_encoder = metal_command_list.GetNativeCommandEncoder(); + [mtl_cmd_encoder setComputePipelineState: GetNativePipelineState()]; +} + +bool ComputeState::SetName(std::string_view name) +{ + META_FUNCTION_TASK(); + if (!Base::ComputeState::SetName(name)) + return false; + + NSString* ns_name = MacOS::ConvertToNsString(name); + m_mtl_pipeline_state_desc.label = ns_name; + + ResetNativeState(); + return true; +} + +void ComputeState::InitializeNativePipelineState() +{ + META_FUNCTION_TASK(); + if (m_mtl_pipeline_state) + return; + + NSError* ns_error = nil; + m_mtl_pipeline_state = [m_device.GetNativeDevice() newComputePipelineStateWithDescriptor: m_mtl_pipeline_state_desc + options: MTLPipelineOptionNone + reflection: nil + error: &ns_error]; + META_CHECK_ARG_NOT_NULL_DESCR(m_mtl_pipeline_state, + "failed to create Metal compute pipeline state: {}", + MacOS::ConvertFromNsString([ns_error localizedDescription])); +} + +id ComputeState::GetNativePipelineState() +{ + META_FUNCTION_TASK(); + if (!m_mtl_pipeline_state) + { + InitializeNativePipelineState(); + } + return m_mtl_pipeline_state; +} + +void ComputeState::ResetNativeState() +{ + META_FUNCTION_TASK(); + m_mtl_pipeline_state = nil; +} + +} // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Device.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Device.mm index b0cb9b1a7..f854dd47d 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Device.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Device.mm @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -54,4 +55,12 @@ return render_context_ptr; } +Ptr Device::CreateComputeContext(tf::Executor& parallel_executor, const Rhi::ComputeContextSettings& settings) +{ + META_FUNCTION_TASK(); + const auto compute_context_ptr = std::make_shared(*this, parallel_executor, settings); + compute_context_ptr->Initialize(*this, true); + return compute_context_ptr; +} + } // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Program.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Program.mm index 21182b3a3..eacbf8a5a 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Program.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Program.mm @@ -38,9 +38,43 @@ Program::Program(const Base::Context& context, const Settings& settings) : Base::Program(context, settings) - , m_mtl_vertex_desc(GetMetalShader(Rhi::ShaderType::Vertex).GetNativeVertexDescriptor(*this)) { META_FUNCTION_TASK(); + if (HasShader(Rhi::ShaderType::Vertex)) + ReflectRenderPipelineArguments(); + else if (HasShader(Rhi::ShaderType::Compute)) + ReflectComputePipelineArguments(); +} + +Ptr Program::CreateBindings(const ResourceViewsByArgument& resource_views_by_argument, Data::Index frame_index) +{ + return std::make_shared(*this, resource_views_by_argument, frame_index); +} + +const IContext& Program::GetMetalContext() const noexcept +{ + META_FUNCTION_TASK(); + return dynamic_cast(GetContext()); +} + +Shader& Program::GetMetalShader(Rhi::ShaderType shader_type) noexcept +{ + META_FUNCTION_TASK(); + return static_cast(GetShaderRef(shader_type)); +} + +id Program::GetNativeShaderFunction(Rhi::ShaderType shader_type) noexcept +{ + META_FUNCTION_TASK(); + return HasShader(shader_type) ? static_cast(GetShaderRef(shader_type)).GetNativeFunction() : nil; +} + +void Program::ReflectRenderPipelineArguments() +{ + META_FUNCTION_TASK(); + const Settings& settings = Base::Program::GetSettings(); + + m_mtl_vertex_desc = GetMetalShader(Rhi::ShaderType::Vertex).GetNativeVertexDescriptor(*this); // Create dummy pipeline state to get program reflection of vertex and fragment shader arguments MTLRenderPipelineDescriptor* mtl_reflection_state_desc = [MTLRenderPipelineDescriptor new]; @@ -58,59 +92,63 @@ mtl_reflection_state_desc.colorAttachments[attachment_index].pixelFormat = MTLPixelFormatInvalid; mtl_reflection_state_desc.depthAttachmentPixelFormat = TypeConverter::DataFormatToMetalPixelType(settings.attachment_formats.depth); mtl_reflection_state_desc.stencilAttachmentPixelFormat = TypeConverter::DataFormatToMetalPixelType(settings.attachment_formats.stencil); - - const IContext& metal_context = dynamic_cast(context); - + NSError* ns_error = nil; - const id& mtl_device = metal_context.GetMetalDevice().GetNativeDevice(); + const id& mtl_device = GetMetalContext().GetMetalDevice().GetNativeDevice(); MTLRenderPipelineReflection* mtl_render_pipeline_reflection = nil; - m_mtl_dummy_pipeline_state_for_reflection = [mtl_device newRenderPipelineStateWithDescriptor:mtl_reflection_state_desc - options:MTLPipelineOptionArgumentInfo - reflection:&mtl_render_pipeline_reflection - error:&ns_error]; + id mtl_render_pipeline_state = [mtl_device newRenderPipelineStateWithDescriptor: mtl_reflection_state_desc + options: MTLPipelineOptionArgumentInfo + reflection: &mtl_render_pipeline_reflection + error: &ns_error]; - META_CHECK_ARG_NOT_NULL_DESCR(m_mtl_dummy_pipeline_state_for_reflection, + META_CHECK_ARG_NOT_NULL_DESCR(mtl_render_pipeline_state, "Failed to create dummy pipeline state for program reflection: {}", MacOS::ConvertFromNsString([ns_error localizedDescription])); - if (mtl_render_pipeline_reflection) - { - SetNativeShaderArguments(Rhi::ShaderType::Vertex, mtl_render_pipeline_reflection.vertexArguments); - SetNativeShaderArguments(Rhi::ShaderType::Pixel, mtl_render_pipeline_reflection.fragmentArguments); - InitArgumentBindings(settings.argument_accessors); - } -} + if (!mtl_render_pipeline_reflection) + return; -Ptr Program::CreateBindings(const ResourceViewsByArgument& resource_views_by_argument, Data::Index frame_index) -{ - return std::make_shared(*this, resource_views_by_argument, frame_index); + SetNativeShaderArguments(Rhi::ShaderType::Vertex, mtl_render_pipeline_reflection.vertexBindings); + SetNativeShaderArguments(Rhi::ShaderType::Pixel, mtl_render_pipeline_reflection.fragmentBindings); + InitArgumentBindings(settings.argument_accessors); } -const IContext& Program::GetMetalContext() const noexcept +void Program::ReflectComputePipelineArguments() { META_FUNCTION_TASK(); - return dynamic_cast(GetContext()); -} + const Settings& settings = Base::Program::GetSettings(); -Shader& Program::GetMetalShader(Rhi::ShaderType shader_type) noexcept -{ - META_FUNCTION_TASK(); - return static_cast(GetShaderRef(shader_type)); -} + // Create dummy pipeline state to get program reflection of vertex and fragment shader arguments + MTLComputePipelineDescriptor* mtl_reflection_state_desc = [MTLComputePipelineDescriptor new]; + mtl_reflection_state_desc.computeFunction = GetNativeShaderFunction(Rhi::ShaderType::Compute); -id Program::GetNativeShaderFunction(Rhi::ShaderType shader_type) noexcept -{ - META_FUNCTION_TASK(); - return HasShader(shader_type) ? static_cast(GetShaderRef(shader_type)).GetNativeFunction() : nil; + NSError* ns_error = nil; + const id& mtl_device = GetMetalContext().GetMetalDevice().GetNativeDevice(); + + MTLComputePipelineReflection* mtl_compute_pipeline_reflection = nil; + id mtl_compute_pipeline_state = [mtl_device newComputePipelineStateWithDescriptor: mtl_reflection_state_desc + options: MTLPipelineOptionArgumentInfo + reflection: &mtl_compute_pipeline_reflection + error: &ns_error]; + + META_CHECK_ARG_NOT_NULL_DESCR(mtl_compute_pipeline_state, + "Failed to create compute pipeline state for program reflection: {}", + MacOS::ConvertFromNsString([ns_error localizedDescription])); + + if (!mtl_compute_pipeline_reflection) + return; + + SetNativeShaderArguments(Rhi::ShaderType::Compute, mtl_compute_pipeline_reflection.bindings); + InitArgumentBindings(settings.argument_accessors); } -void Program::SetNativeShaderArguments(Rhi::ShaderType shader_type, NSArray* mtl_arguments) noexcept +void Program::SetNativeShaderArguments(Rhi::ShaderType shader_type, NSArray>* mtl_arguments) noexcept { META_FUNCTION_TASK(); if (HasShader(shader_type)) { - GetMetalShader(shader_type).SetNativeArguments(mtl_arguments); + GetMetalShader(shader_type).SetNativeBindings(mtl_arguments); } } diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ProgramBindings.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ProgramBindings.mm index e46b1dbcc..62ff7240a 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ProgramBindings.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ProgramBindings.mm @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -39,14 +40,22 @@ using NativeSamplerStates = ProgramArgumentBinding::NativeSamplerStates; using NativeOffsets = ProgramArgumentBinding::NativeOffsets; +constexpr ProgramBindings::ApplyBehaviorMask g_constant_once_and_changes_only({ + ProgramBindings::ApplyBehavior::ConstantOnce, + ProgramBindings::ApplyBehavior::ChangesOnly +}); + template -void SetMetalResource(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, TMetalResource mtl_buffer, uint32_t arg_index, NSUInteger buffer_offset); +void SetRenderResource(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, + TMetalResource mtl_buffer, uint32_t arg_index, NSUInteger buffer_offset); template -void SetMetalResources(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, const std::vector& mtl_buffer, uint32_t arg_index, const std::vector& buffer_offsets); +void SetRenderResources(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, + const std::vector& mtl_buffer, uint32_t arg_index, const std::vector& buffer_offsets); template<> -void SetMetalResource(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, __unsafe_unretained id mtl_buffer, uint32_t arg_index, NSUInteger buffer_offset) +void SetRenderResource(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, + __unsafe_unretained id mtl_buffer, uint32_t arg_index, NSUInteger buffer_offset) { META_FUNCTION_TASK(); switch(shader_type) @@ -58,8 +67,8 @@ void SetMetalResource(Rhi::ShaderType shader_type, const id -void SetMetalResources(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, const NativeBuffers& mtl_buffers, - uint32_t arg_index, const std::vector& buffer_offsets) +void SetRenderResources(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, + const NativeBuffers& mtl_buffers, uint32_t arg_index, const std::vector& buffer_offsets) { META_FUNCTION_TASK(); const NSRange args_range = NSMakeRange(arg_index, mtl_buffers.size()); @@ -72,7 +81,8 @@ void SetMetalResources(Rhi::ShaderType shader_type, const id -void SetMetalResource(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, __unsafe_unretained id mtl_texture, uint32_t arg_index, NSUInteger) +void SetRenderResource(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, + __unsafe_unretained id mtl_texture, uint32_t arg_index, NSUInteger) { META_FUNCTION_TASK(); switch(shader_type) @@ -84,8 +94,8 @@ void SetMetalResource(Rhi::ShaderType shader_type, const id -void SetMetalResources(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, const NativeTextures& mtl_textures, - uint32_t arg_index, const std::vector&) +void SetRenderResources(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, + const NativeTextures& mtl_textures, uint32_t arg_index, const std::vector&) { META_FUNCTION_TASK(); const NSRange args_range = NSMakeRange(arg_index, mtl_textures.size()); @@ -98,7 +108,8 @@ void SetMetalResources(Rhi::ShaderType shader_type, const id -void SetMetalResource(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, __unsafe_unretained id mtl_sampler, uint32_t arg_index, NSUInteger) +void SetRenderResource(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, + __unsafe_unretained id mtl_sampler, uint32_t arg_index, NSUInteger) { META_FUNCTION_TASK(); switch(shader_type) @@ -110,8 +121,8 @@ void SetMetalResource(Rhi::ShaderType shader_type, const id -void SetMetalResources(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, const NativeSamplerStates& mtl_samplers, - uint32_t arg_index, const std::vector&) +void SetRenderResources(Rhi::ShaderType shader_type, const id& mtl_cmd_encoder, + const NativeSamplerStates& mtl_samplers, uint32_t arg_index, const std::vector&) { META_FUNCTION_TASK(); const NSRange args_range = NSMakeRange(arg_index, mtl_samplers.size()); @@ -124,7 +135,7 @@ void SetMetalResources(Rhi::ShaderType shader_type, const id -void SetMetalResourcesForAll(Rhi::ShaderType shader_type, const Rhi::IProgram& program, const id& mtl_cmd_encoder, +void SetRenderResourcesForAll(Rhi::ShaderType shader_type, const Rhi::IProgram& program, const id& mtl_cmd_encoder, const std::vector& mtl_resources, uint32_t arg_index, const std::vector& offsets = std::vector()) { @@ -137,14 +148,14 @@ void SetMetalResourcesForAll(Rhi::ShaderType shader_type, const Rhi::IProgram& p { for (Rhi::ShaderType specific_shader_type : program.GetShaderTypes()) { - SetMetalResources(specific_shader_type, mtl_cmd_encoder, mtl_resources, arg_index, offsets); + SetRenderResources(specific_shader_type, mtl_cmd_encoder, mtl_resources, arg_index, offsets); } } else { for (Rhi::ShaderType specific_shader_type : program.GetShaderTypes()) { - SetMetalResource(specific_shader_type, mtl_cmd_encoder, mtl_resources.back(), arg_index, + SetRenderResource(specific_shader_type, mtl_cmd_encoder, mtl_resources.back(), arg_index, offsets.empty() ? 0 : offsets.back()); } } @@ -153,16 +164,93 @@ void SetMetalResourcesForAll(Rhi::ShaderType shader_type, const Rhi::IProgram& p { if (mtl_resources.size() > 1) { - SetMetalResources(shader_type, mtl_cmd_encoder, mtl_resources, arg_index, offsets); + SetRenderResources(shader_type, mtl_cmd_encoder, mtl_resources, arg_index, offsets); } else { - SetMetalResource(shader_type, mtl_cmd_encoder, mtl_resources.back(), arg_index, + SetRenderResource(shader_type, mtl_cmd_encoder, mtl_resources.back(), arg_index, offsets.empty() ? 0 : offsets.back()); } } } +template +void SetComputeResource(const id& mtl_cmd_encoder, + TMetalResource mtl_buffer, uint32_t arg_index, NSUInteger buffer_offset); + +template +void SetComputeResources(const id& mtl_cmd_encoder, + const std::vector& mtl_buffer, uint32_t arg_index, const std::vector& buffer_offsets); + +template<> +void SetComputeResource(const id& mtl_cmd_encoder, + __unsafe_unretained id mtl_buffer, uint32_t arg_index, NSUInteger buffer_offset) +{ + META_FUNCTION_TASK(); + [mtl_cmd_encoder setBuffer:mtl_buffer offset:buffer_offset atIndex:arg_index]; +} + +template<> +void SetComputeResources(const id& mtl_cmd_encoder, + const NativeBuffers& mtl_buffers, uint32_t arg_index, const std::vector& buffer_offsets) +{ + META_FUNCTION_TASK(); + const NSRange args_range = NSMakeRange(arg_index, mtl_buffers.size()); + [mtl_cmd_encoder setBuffers:mtl_buffers.data() offsets:buffer_offsets.data() withRange:args_range]; +} + +template<> +void SetComputeResource(const id& mtl_cmd_encoder, + __unsafe_unretained id mtl_texture, uint32_t arg_index, NSUInteger) +{ + META_FUNCTION_TASK(); + [mtl_cmd_encoder setTexture:mtl_texture atIndex:arg_index]; +} + +template<> +void SetComputeResources(const id& mtl_cmd_encoder, + const NativeTextures& mtl_textures, uint32_t arg_index, const std::vector&) +{ + META_FUNCTION_TASK(); + const NSRange args_range = NSMakeRange(arg_index, mtl_textures.size()); + [mtl_cmd_encoder setTextures:mtl_textures.data() withRange:args_range]; +} + +template<> +void SetComputeResource(const id& mtl_cmd_encoder, + __unsafe_unretained id mtl_sampler, uint32_t arg_index, NSUInteger) +{ + META_FUNCTION_TASK(); + [mtl_cmd_encoder setSamplerState:mtl_sampler atIndex:arg_index]; +} + +template<> +void SetComputeResources(const id& mtl_cmd_encoder, + const NativeSamplerStates& mtl_samplers, uint32_t arg_index, const std::vector&) +{ + META_FUNCTION_TASK(); + const NSRange args_range = NSMakeRange(arg_index, mtl_samplers.size()); + [mtl_cmd_encoder setSamplerStates:mtl_samplers.data() withRange:args_range]; +} + +template +void SetComputeResourcesForAll(const id& mtl_cmd_encoder, + const std::vector& mtl_resources, uint32_t arg_index, + const std::vector& offsets = std::vector()) +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_NOT_EMPTY(mtl_resources); + if (mtl_resources.size() > 1) + { + SetComputeResources(mtl_cmd_encoder, mtl_resources, arg_index, offsets); + } + else + { + SetComputeResource(mtl_cmd_encoder, mtl_resources.back(), arg_index, + offsets.empty() ? 0 : offsets.back()); + } +} + ProgramBindings::ProgramBindings(Program& program, const ResourceViewsByArgument& resource_views_by_argument, Data::Index frame_index) : Base::ProgramBindings(program, resource_views_by_argument, frame_index) { } @@ -180,44 +268,88 @@ void SetMetalResourcesForAll(Rhi::ShaderType shader_type, const Rhi::IProgram& p void ProgramBindings::Apply(Base::CommandList& command_list, ApplyBehaviorMask apply_behavior) const { META_FUNCTION_TASK(); - RenderCommandList& metal_command_list = static_cast(command_list); - const id& mtl_cmd_encoder = metal_command_list.GetNativeCommandEncoder(); - constexpr ApplyBehaviorMask constant_once_and_changes_only({ - ApplyBehavior::ConstantOnce, - ApplyBehavior::ChangesOnly - }); - + const Rhi::CommandListType command_list_type = command_list.GetType(); + switch(command_list_type) + { + case Rhi::CommandListType::Render: Apply(static_cast(command_list), apply_behavior); break; + case Rhi::CommandListType::Compute: Apply(static_cast(command_list), apply_behavior); break; + default: META_UNEXPECTED_ARG(command_list_type); + } +} + +template // function void(const ArgumentBinding&) +void ProgramBindings::ForEachChangedArgumentBinding(const Base::ProgramBindings* applied_program_bindings_ptr, ApplyBehaviorMask apply_behavior, FuncType functor) const +{ for(const auto& binding_by_argument : GetArgumentBindings()) { - const Rhi::IProgram::Argument& program_argument = binding_by_argument.first; const ArgumentBinding& metal_argument_binding = static_cast(*binding_by_argument.second); - - if (apply_behavior.HasAnyBits(constant_once_and_changes_only) && - metal_command_list.GetProgramBindingsPtr() && - metal_argument_binding.IsAlreadyApplied(GetProgram(), *metal_command_list.GetProgramBindingsPtr(), - apply_behavior.HasAnyBit(ApplyBehavior::ChangesOnly))) + if (apply_behavior.HasAnyBits(g_constant_once_and_changes_only) && applied_program_bindings_ptr && + metal_argument_binding.IsAlreadyApplied(GetProgram(), *applied_program_bindings_ptr, apply_behavior.HasAnyBit(ApplyBehavior::ChangesOnly))) continue; - const uint32_t arg_index = metal_argument_binding.GetMetalSettings().argument_index; + functor(metal_argument_binding); + } +} + +void ProgramBindings::Apply(RenderCommandList& render_command_list, ApplyBehaviorMask apply_behavior) const +{ + META_FUNCTION_TASK(); + const id& mtl_cmd_encoder = render_command_list.GetNativeCommandEncoder(); + Rhi::IProgram& program = GetProgram(); - switch(metal_argument_binding.GetMetalSettings().resource_type) + ForEachChangedArgumentBinding(render_command_list.GetProgramBindingsPtr(), apply_behavior, + [&mtl_cmd_encoder, &program](const ArgumentBinding& argument_binding) { + const ProgramArgumentBinding::Settings& settings = argument_binding.GetMetalSettings(); + switch(settings.resource_type) + { case Rhi::ResourceType::Buffer: - SetMetalResourcesForAll(program_argument.GetShaderType(), GetProgram(), mtl_cmd_encoder, metal_argument_binding.GetNativeBuffers(), arg_index, - metal_argument_binding.GetBufferOffsets()); + SetRenderResourcesForAll(settings.argument.GetShaderType(), program, mtl_cmd_encoder, argument_binding.GetNativeBuffers(), settings.argument_index, + argument_binding.GetBufferOffsets()); break; case Rhi::ResourceType::Texture: - SetMetalResourcesForAll(program_argument.GetShaderType(), GetProgram(), mtl_cmd_encoder, metal_argument_binding.GetNativeTextures(), arg_index); + SetRenderResourcesForAll(settings.argument.GetShaderType(), program, mtl_cmd_encoder, argument_binding.GetNativeTextures(), settings.argument_index); break; case Rhi::ResourceType::Sampler: - SetMetalResourcesForAll(program_argument.GetShaderType(), GetProgram(), mtl_cmd_encoder, metal_argument_binding.GetNativeSamplerStates(), arg_index); + SetRenderResourcesForAll(settings.argument.GetShaderType(), program, mtl_cmd_encoder, argument_binding.GetNativeSamplerStates(), settings.argument_index); break; - default: META_UNEXPECTED_ARG(metal_argument_binding.GetMetalSettings().resource_type); - } - } + default: + META_UNEXPECTED_ARG(settings.resource_type); + } + }); +} + +void ProgramBindings::Apply(ComputeCommandList& compute_command_list, ApplyBehaviorMask apply_behavior) const +{ + META_FUNCTION_TASK(); + const id& mtl_cmd_encoder = compute_command_list.GetNativeCommandEncoder(); + + ForEachChangedArgumentBinding(compute_command_list.GetProgramBindingsPtr(), apply_behavior, + [&mtl_cmd_encoder](const ArgumentBinding& argument_binding) + { + const ProgramArgumentBinding::Settings& settings = argument_binding.GetMetalSettings(); + + switch(settings.resource_type) + { + case Rhi::ResourceType::Buffer: + SetComputeResourcesForAll(mtl_cmd_encoder, argument_binding.GetNativeBuffers(), settings.argument_index, argument_binding.GetBufferOffsets()); + break; + + case Rhi::ResourceType::Texture: + SetComputeResourcesForAll(mtl_cmd_encoder, argument_binding.GetNativeTextures(), settings.argument_index); + break; + + case Rhi::ResourceType::Sampler: + SetComputeResourcesForAll(mtl_cmd_encoder, argument_binding.GetNativeSamplerStates(), settings.argument_index); + break; + + default: + META_UNEXPECTED_ARG(settings.resource_type); + } + }); } } // namespace Methane::Graphics::Metal diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ProgramLibrary.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ProgramLibrary.mm index 5ac1aff3f..b2b27fa10 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ProgramLibrary.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/ProgramLibrary.mm @@ -50,7 +50,8 @@ else { NSError* ns_error = nil; - m_mtl_library = [metal_device.GetNativeDevice() newLibraryWithFile:GetLibraryFullPath(library_name) error:&ns_error]; + NSURL* library_url = [NSURL fileURLWithPath:GetLibraryFullPath(library_name)]; + m_mtl_library = [metal_device.GetNativeDevice() newLibraryWithURL:library_url error:&ns_error]; META_CHECK_ARG_NOT_NULL_DESCR(m_mtl_library, "Failed to create {} Metal library: {}", library_name.empty() ? std::string_view("default") : library_name, diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/RenderContext.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/RenderContext.mm index 73098c438..a8dc79078 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/RenderContext.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/RenderContext.mm @@ -22,15 +22,9 @@ ******************************************************************************/ #include -#include -#include -#include #include #include #include -#include -#include -#include #include #include @@ -81,42 +75,6 @@ #endif } -Ptr RenderContext::CreateCommandQueue(Rhi::CommandListType type) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, type); -} - -Ptr RenderContext::CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(type, *this, settings); -} - -Ptr RenderContext::CreateProgram(const Rhi::ProgramSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateBuffer(const Rhi::BufferSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateTexture(const Rhi::TextureSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateSampler(const Rhi::SamplerSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - Ptr RenderContext::CreateRenderState(const Rhi::RenderStateSettings& settings) const { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/RenderState.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/RenderState.mm index 17aec2317..f7525ef6e 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/RenderState.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/RenderState.mm @@ -187,37 +187,37 @@ static MTLWinding ConvertRasterizerFrontWindingToMetal(bool is_front_counter_clo Program& metal_program = static_cast(*settings.program_ptr); // IProgram state - m_mtl_pipeline_state_desc = [[MTLRenderPipelineDescriptor alloc] init]; - m_mtl_pipeline_state_desc.vertexFunction = metal_program.GetNativeShaderFunction(Rhi::ShaderType::Vertex); - m_mtl_pipeline_state_desc.fragmentFunction = metal_program.GetNativeShaderFunction(Rhi::ShaderType::Pixel); - m_mtl_pipeline_state_desc.vertexDescriptor = metal_program.GetNativeVertexDescriptor(); + m_mtl_pipeline_state_desc = [[MTLRenderPipelineDescriptor alloc] init]; + m_mtl_pipeline_state_desc.vertexFunction = metal_program.GetNativeShaderFunction(Rhi::ShaderType::Vertex); + m_mtl_pipeline_state_desc.fragmentFunction = metal_program.GetNativeShaderFunction(Rhi::ShaderType::Pixel); + m_mtl_pipeline_state_desc.vertexDescriptor = metal_program.GetNativeVertexDescriptor(); // Rasterizer state - m_mtl_pipeline_state_desc.sampleCount = settings.rasterizer.sample_count; - m_mtl_pipeline_state_desc.alphaToCoverageEnabled = settings.rasterizer.alpha_to_coverage_enabled; - m_mtl_pipeline_state_desc.alphaToOneEnabled = NO; // not supported by Methane + m_mtl_pipeline_state_desc.rasterSampleCount = settings.rasterizer.sample_count; + m_mtl_pipeline_state_desc.alphaToCoverageEnabled = settings.rasterizer.alpha_to_coverage_enabled; + m_mtl_pipeline_state_desc.alphaToOneEnabled = NO; // not supported by Methane // Blending state const AttachmentFormats attach_formats = settings.render_pattern_ptr->GetAttachmentFormats(); for (uint32_t rt_index = 0; rt_index < settings.blending.render_targets.size(); ++rt_index) { - const Blending::RenderTarget& render_target = settings.blending.is_independent - ? settings.blending.render_targets[rt_index] - : settings.blending.render_targets[0]; + const Blending::RenderTarget& render_target = settings.blending.is_independent + ? settings.blending.render_targets[rt_index] + : settings.blending.render_targets[0]; // Set render target blending state for color attachment MTLRenderPipelineColorAttachmentDescriptor* mtl_color_attach = m_mtl_pipeline_state_desc.colorAttachments[rt_index]; - mtl_color_attach.pixelFormat = rt_index < attach_formats.colors.size() - ? TypeConverter::DataFormatToMetalPixelType(attach_formats.colors[rt_index]) - : MTLPixelFormatInvalid; - mtl_color_attach.blendingEnabled = render_target.blend_enabled && rt_index < attach_formats.colors.size(); - mtl_color_attach.writeMask = ConvertRenderTargetColorWriteMaskToMetal(render_target.color_write); - mtl_color_attach.rgbBlendOperation = ConvertBlendingOperationToMetal(render_target.rgb_blend_op); - mtl_color_attach.alphaBlendOperation = ConvertBlendingOperationToMetal(render_target.alpha_blend_op); - mtl_color_attach.sourceRGBBlendFactor = ConvertBlendingFactorToMetal(render_target.source_rgb_blend_factor); - mtl_color_attach.sourceAlphaBlendFactor = ConvertBlendingFactorToMetal(render_target.source_alpha_blend_factor); - mtl_color_attach.destinationRGBBlendFactor = ConvertBlendingFactorToMetal(render_target.dest_rgb_blend_factor); - mtl_color_attach.destinationAlphaBlendFactor = ConvertBlendingFactorToMetal(render_target.dest_alpha_blend_factor); + mtl_color_attach.pixelFormat = rt_index < attach_formats.colors.size() + ? TypeConverter::DataFormatToMetalPixelType(attach_formats.colors[rt_index]) + : MTLPixelFormatInvalid; + mtl_color_attach.blendingEnabled = render_target.blend_enabled && rt_index < attach_formats.colors.size(); + mtl_color_attach.writeMask = ConvertRenderTargetColorWriteMaskToMetal(render_target.color_write); + mtl_color_attach.rgbBlendOperation = ConvertBlendingOperationToMetal(render_target.rgb_blend_op); + mtl_color_attach.alphaBlendOperation = ConvertBlendingOperationToMetal(render_target.alpha_blend_op); + mtl_color_attach.sourceRGBBlendFactor = ConvertBlendingFactorToMetal(render_target.source_rgb_blend_factor); + mtl_color_attach.sourceAlphaBlendFactor = ConvertBlendingFactorToMetal(render_target.source_alpha_blend_factor); + mtl_color_attach.destinationRGBBlendFactor = ConvertBlendingFactorToMetal(render_target.dest_rgb_blend_factor); + mtl_color_attach.destinationAlphaBlendFactor = ConvertBlendingFactorToMetal(render_target.dest_alpha_blend_factor); } // Color, depth, stencil attachment formats state from program settings @@ -301,7 +301,7 @@ static MTLWinding ConvertRasterizerFrontWindingToMetal(bool is_front_counter_clo NSError* ns_error = nil; m_mtl_pipeline_state = [GetMetalRenderContext().GetMetalDevice().GetNativeDevice() newRenderPipelineStateWithDescriptor:m_mtl_pipeline_state_desc error:&ns_error]; META_CHECK_ARG_NOT_NULL_DESCR(m_mtl_pipeline_state, - "failed to create Metal pipeline state: {}", + "failed to create Metal render pipeline state: {}", MacOS::ConvertFromNsString([ns_error localizedDescription])); } @@ -313,7 +313,7 @@ static MTLWinding ConvertRasterizerFrontWindingToMetal(bool is_front_counter_clo META_CHECK_ARG_NOT_NULL(m_mtl_depth_stencil_state_desc); m_mtl_depth_state = [GetMetalRenderContext().GetMetalDevice().GetNativeDevice() newDepthStencilStateWithDescriptor:m_mtl_depth_stencil_state_desc]; - META_CHECK_ARG_NOT_NULL_DESCR(m_mtl_depth_state, "failed to create Metal depth state"); + META_CHECK_ARG_NOT_NULL_DESCR(m_mtl_depth_state, "failed to create Metal depth-stencil state"); } id RenderState::GetNativePipelineState() diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Shader.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Shader.mm index 95c166029..9b1fd3558 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Shader.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Shader.mm @@ -21,6 +21,7 @@ ******************************************************************************/ +#include "Metal/Metal.h" #include #include #include @@ -58,30 +59,40 @@ static MTLVertexStepFunction GetVertexStepFunction(StepType step_type) } [[nodiscard]] -static Rhi::IResource::Type GetResourceTypeByMetalArgumentType(MTLArgumentType mtl_arg_type) +static Rhi::IResource::Type GetResourceTypeByMetalArgumentType(MTLBindingType mtl_arg_type) { META_FUNCTION_TASK(); switch(mtl_arg_type) { - case MTLArgumentTypeBuffer: return Rhi::ResourceType::Buffer; - case MTLArgumentTypeTexture: return Rhi::ResourceType::Texture; - case MTLArgumentTypeSampler: return Rhi::ResourceType::Sampler; - default: META_UNEXPECTED_ARG_RETURN(mtl_arg_type, IResource::Type::Buffer); + case MTLBindingTypeBuffer: return Rhi::ResourceType::Buffer; + case MTLBindingTypeTexture: return Rhi::ResourceType::Texture; + case MTLBindingTypeSampler: return Rhi::ResourceType::Sampler; + default: META_UNEXPECTED_ARG_RETURN(mtl_arg_type, IResource::Type::Buffer); } } +[[nodiscard]] +static uint32_t GetBindingArrayLength(id mtl_binding) +{ + if (mtl_binding.type != MTLBindingTypeTexture) + return 1U; + + id mtl_texture_binding = static_cast>(mtl_binding); + return static_cast(mtl_texture_binding.arrayLength); +} + #ifndef NDEBUG [[nodiscard]] -static std::string GetMetalArgumentTypeName(MTLArgumentType mtl_arg_type) +static std::string GetMetalArgumentTypeName(MTLBindingType mtl_arg_type) { META_FUNCTION_TASK(); switch(mtl_arg_type) { - case MTLArgumentTypeBuffer: return "Buffer"; - case MTLArgumentTypeThreadgroupMemory: return "Thread-group Memory"; - case MTLArgumentTypeTexture: return "Texture"; - case MTLArgumentTypeSampler: return "Sampler"; + case MTLBindingTypeBuffer: return "Buffer"; + case MTLBindingTypeThreadgroupMemory: return "Thread-group Memory"; + case MTLBindingTypeTexture: return "Texture"; + case MTLBindingTypeSampler: return "Sampler"; default: META_UNEXPECTED_ARG_RETURN(mtl_arg_type, "Unknown"); } } @@ -114,19 +125,19 @@ static MTLVertexStepFunction GetVertexStepFunction(StepType step_type) { META_FUNCTION_TASK(); Ptrs argument_bindings; - if (m_mtl_arguments == nil) + if (m_mtl_bindings == nil) return argument_bindings; #ifndef NDEBUG NSLog(@"%s shader '%s' arguments:", magic_enum::enum_name(GetType()).data(), GetCompiledEntryFunctionName().c_str()); #endif - for(MTLArgument* mtl_arg in m_mtl_arguments) + for(id mtl_binding in m_mtl_bindings) { - if (!mtl_arg.active) + if (!mtl_binding.argument || !mtl_binding.used) continue; - const std::string argument_name = MacOS::ConvertFromNsString(mtl_arg.name); + const std::string argument_name = MacOS::ConvertFromNsString(mtl_binding.name); if (argument_name.find("vertexBuffer.") == 0) { // Skip input vertex buffers, since they are set with a separate IRenderCommandList call, not through resource bindings @@ -138,30 +149,31 @@ static MTLVertexStepFunction GetVertexStepFunction(StepType step_type) const Rhi::ProgramArgumentAccessor argument_desc = argument_desc_it == argument_accessors.end() ? Rhi::ProgramArgumentAccessor(shader_argument) : *argument_desc_it; - + + const uint32_t array_length = GetBindingArrayLength(mtl_binding); argument_bindings.emplace_back(std::make_shared( GetContext(), ProgramArgumentBindingSettings { { argument_desc, - GetResourceTypeByMetalArgumentType(mtl_arg.type), - static_cast(mtl_arg.arrayLength), + GetResourceTypeByMetalArgumentType(mtl_binding.type), + array_length, }, - static_cast(mtl_arg.index) + static_cast(mtl_binding.index) } )); #ifndef NDEBUG - const std::string mtl_arg_type_name = GetMetalArgumentTypeName(mtl_arg.type); - const std::string mtl_arg_access_name = GetMetalArgumentAccessName(mtl_arg.access); - if (mtl_arg.arrayLength <= 1) + const std::string mtl_arg_type_name = GetMetalArgumentTypeName(mtl_binding.type); + const std::string mtl_arg_access_name = GetMetalArgumentAccessName(mtl_binding.access); + if (array_length <= 1) { - NSLog(@" - %s (%s) with name \"%@\" at index %u", mtl_arg_type_name.c_str(), mtl_arg_access_name.c_str(), mtl_arg.name, (uint32_t) mtl_arg.index); + NSLog(@" - %s (%s) with name \"%@\" at index %u", mtl_arg_type_name.c_str(), mtl_arg_access_name.c_str(), mtl_binding.name, (uint32_t) mtl_binding.index); } else { - NSLog(@" - %s (%s) array of size %u with name \"%@\" at index %u", mtl_arg_type_name.c_str(), mtl_arg_access_name.c_str(), (uint32_t) mtl_arg.arrayLength, mtl_arg.name, (uint32_t) mtl_arg.index); + NSLog(@" - %s (%s) array of size %u with name \"%@\" at index %u", mtl_arg_type_name.c_str(), mtl_arg_access_name.c_str(), array_length, mtl_binding.name, (uint32_t) mtl_binding.index); } #endif } diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/System.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/System.mm index 8aba70597..36a5fb33a 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/System.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/System.mm @@ -114,7 +114,7 @@ { META_FUNCTION_TASK(); const Rhi::DeviceFeatureMask device_supported_features = Device::GetSupportedFeatures(mtl_device); - if (!(device_supported_features & GetDeviceCapabilities().features)) + if (!device_supported_features.HasBits(GetDeviceCapabilities().features)) return; Base::System::AddDevice(std::make_shared(mtl_device, GetDeviceCapabilities())); diff --git a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Texture.mm b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Texture.mm index 307b63ade..38223eaea 100644 --- a/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Texture.mm +++ b/Modules/Graphics/RHI/Metal/Sources/Methane/Graphics/Metal/Texture.mm @@ -92,13 +92,13 @@ static MTLRegion GetTextureRegion(const Dimensions& dimensions, Rhi::TextureDime return true; } -void Texture::SetData(const SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) +void Texture::SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResources& sub_resources) { META_FUNCTION_TASK(); META_CHECK_ARG_NOT_NULL(m_mtl_texture); META_CHECK_ARG_EQUAL(m_mtl_texture.storageMode, MTLStorageModePrivate); - Resource::SetData(sub_resources, target_cmd_queue); + Base::Texture::SetData(target_cmd_queue, sub_resources); TransferCommandList& transfer_command_list = dynamic_cast(GetBaseContext().GetUploadCommandKit().GetListForEncoding()); transfer_command_list.RetainResource(*this); @@ -130,7 +130,7 @@ static MTLRegion GetTextureRegion(const Dimensions& dimensions, Rhi::TextureDime slice = 0; } - [mtl_blit_encoder copyFromBuffer:GetUploadSubresourceBuffer(sub_resource) + [mtl_blit_encoder copyFromBuffer:GetUploadSubresourceBuffer(sub_resource, GetSubresourceCount()) sourceOffset:0 sourceBytesPerRow:bytes_per_row sourceBytesPerImage:bytes_per_image @@ -149,6 +149,50 @@ static MTLRegion GetTextureRegion(const Dimensions& dimensions, Rhi::TextureDime GetBaseContext().RequestDeferredAction(Rhi::IContext::DeferredAction::UploadResources); } +Rhi::SubResource Texture::GetData(Rhi::ICommandQueue&, const SubResource::Index& sub_resource_index, const BytesRangeOpt& data_range) +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_NOT_NULL(m_mtl_texture); + META_CHECK_ARG_EQUAL(m_mtl_texture.storageMode, MTLStorageModePrivate); + + ValidateSubResource(sub_resource_index, data_range); + + TransferCommandList& transfer_command_list = dynamic_cast(GetBaseContext().GetUploadCommandKit().GetListForEncoding()); + transfer_command_list.RetainResource(*this); + + const id& mtl_blit_encoder = transfer_command_list.GetNativeCommandEncoder(); + META_CHECK_ARG_NOT_NULL(mtl_blit_encoder); + + const Settings& settings = GetSettings(); + const uint32_t bytes_per_row = settings.dimensions.GetWidth() * GetPixelSize(settings.pixel_format); + const uint32_t bytes_per_image = settings.dimensions.GetHeight() * bytes_per_row; + const MTLRegion texture_region = GetTextureRegion(settings.dimensions, settings.dimension_type); + + const id mtl_read_back_buffer = GetReadBackBuffer(bytes_per_image); + [mtl_blit_encoder copyFromTexture: m_mtl_texture + sourceSlice: sub_resource_index.GetDepthSlice() + sourceLevel: sub_resource_index.GetMipLevel() + sourceOrigin: texture_region.origin + sourceSize: texture_region.size + toBuffer: mtl_read_back_buffer + destinationOffset: 0U + destinationBytesPerRow: bytes_per_row + destinationBytesPerImage: bytes_per_image]; + + GetBaseContext().UploadResources(); + + auto* data_ptr = reinterpret_cast([mtl_read_back_buffer contents]); + Data::Size data_size = static_cast([mtl_read_back_buffer length]); + if (data_range.has_value()) + { + META_CHECK_ARG_LESS_DESCR(data_range->GetEnd(), data_size, "provided texture subresource data range is out of bounds"); + data_ptr += data_range->GetStart(); + data_size = data_range->GetLength(); + } + + return Rhi::SubResource(Data::Bytes(data_ptr, data_ptr + data_size), sub_resource_index, data_range); +} + void Texture::UpdateFrameBuffer() { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Null/CMakeLists.txt b/Modules/Graphics/RHI/Null/CMakeLists.txt index c7cebf208..781d6584f 100644 --- a/Modules/Graphics/RHI/Null/CMakeLists.txt +++ b/Modules/Graphics/RHI/Null/CMakeLists.txt @@ -16,6 +16,7 @@ list(APPEND HEADERS ${INCLUDE_DIR}/RenderContext.h ${INCLUDE_DIR}/RenderState.h ${INCLUDE_DIR}/ViewState.h + ${INCLUDE_DIR}/ComputeState.h ${INCLUDE_DIR}/ResourceView.h ${INCLUDE_DIR}/ResourceBarriers.h ${INCLUDE_DIR}/Resource.hpp @@ -31,6 +32,7 @@ list(APPEND HEADERS ${INCLUDE_DIR}/CommandListDebugGroup.h ${INCLUDE_DIR}/CommandList.hpp ${INCLUDE_DIR}/TransferCommandList.h + ${INCLUDE_DIR}/ComputeCommandList.h ${INCLUDE_DIR}/RenderCommandList.h ${INCLUDE_DIR}/ParallelRenderCommandList.h ) @@ -55,6 +57,7 @@ list(APPEND SOURCES ${SOURCES_DIR}/CommandListSet.cpp ${SOURCES_DIR}/CommandListDebugGroup.cpp ${SOURCES_DIR}/TransferCommandList.cpp + ${SOURCES_DIR}/ComputeCommandList.cpp ${SOURCES_DIR}/RenderCommandList.cpp ${SOURCES_DIR}/ParallelRenderCommandList.cpp ) diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Buffer.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Buffer.h index 3564dcfff..af7e36099 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Buffer.h +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Buffer.h @@ -35,6 +35,8 @@ class Buffer final // NOSONAR - inheritance hierarchy is greater than 5 { public: Buffer(const Base::Context& context, const Settings& settings); + + SubResource GetData(Rhi::ICommandQueue&, const BytesRangeOpt&) override; }; } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/CommandListSet.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/CommandListSet.h index a2b3f65ee..8b108712d 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/CommandListSet.h +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/CommandListSet.h @@ -36,6 +36,8 @@ class CommandListSet final public: CommandListSet(const Refs& command_list_refs, Opt frame_index_opt); + using Base::CommandListSet::Complete; + // Base::CommandListSet interface void WaitUntilCompleted() override; }; diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/CommandQueue.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/CommandQueue.h index 540f01478..999bc65c2 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/CommandQueue.h +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/CommandQueue.h @@ -41,6 +41,7 @@ class CommandQueue final // ICommandQueue interface [[nodiscard]] Ptr CreateFence() override; [[nodiscard]] Ptr CreateTransferCommandList() override; + [[nodiscard]] Ptr CreateComputeCommandList() override; [[nodiscard]] Ptr CreateRenderCommandList(Rhi::IRenderPass& render_pass) override; [[nodiscard]] Ptr CreateParallelRenderCommandList(Rhi::IRenderPass& render_pass) override; [[nodiscard]] Ptr CreateTimestampQueryPool(uint32_t max_timestamps_per_frame) override; diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/ComputeCommandList.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/ComputeCommandList.h new file mode 100644 index 000000000..790eed59b --- /dev/null +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/ComputeCommandList.h @@ -0,0 +1,47 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Null/ComputeCommandList.h +Null implementation of the compute command list interface. + +******************************************************************************/ + +#pragma once + +#include "CommandList.hpp" + +#include + +namespace Methane::Graphics::Null +{ + +class CommandQueue; + +class ComputeCommandList final // NOSONAR - inheritance hierarchy depth is higher than 5 + : public CommandList +{ +public: + explicit ComputeCommandList(CommandQueue& command_queue); + + void Dispatch(const Rhi::ThreadGroupsCount& thread_groups_count) override; + +private: + Rhi::ThreadGroupsCount m_dispatched_thread_groups_count; +}; + +} // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/ComputeContext.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/ComputeContext.h new file mode 100644 index 000000000..60acacefb --- /dev/null +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/ComputeContext.h @@ -0,0 +1,40 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Null/ComputeContext.hh +Null implementation of the compute context interface. + +******************************************************************************/ + +#pragma once + +#include "Context.hpp" + +#include + +namespace Methane::Graphics::Null +{ + +class ComputeContext final // NOSONAR - inheritance hierarchy depth is higher than 5 + : public Context +{ +public: + using Context::Context; +}; + +} // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/ComputeState.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/ComputeState.h new file mode 100644 index 000000000..8256b8e17 --- /dev/null +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/ComputeState.h @@ -0,0 +1,41 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Null/ComputeState.h +Null implementation of the compute state interface. + +******************************************************************************/ + +#pragma once + +#include + +namespace Methane::Graphics::Null +{ + +class ComputeState final + : public Base::ComputeState +{ +public: + using Base::ComputeState::ComputeState; + + // Base::ComputeState interface + void Apply(Base::ComputeCommandList&) override { /* Intentionally unimplemented */ } +}; + +} // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Context.hpp b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Context.hpp index 90d2633d8..346de95ca 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Context.hpp +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Context.hpp @@ -23,6 +23,14 @@ Null template implementation of the base context interface. #pragma once +#include "CommandQueue.h" +#include "Shader.h" +#include "Program.h" +#include "ComputeState.h" +#include "Buffer.h" +#include "Texture.h" +#include "Sampler.h" + #include #include #include @@ -39,6 +47,43 @@ class Context : ContextBaseT(device, std::make_unique(*this), parallel_executor, settings) { } + + // IContext overrides + + [[nodiscard]] Ptr CreateCommandQueue(Rhi::CommandListType type) const final + { + return std::make_shared(*this, type); + } + + [[nodiscard]] Ptr CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const final + { + return std::make_shared(type, *this, settings); + } + + [[nodiscard]] Ptr CreateProgram(const Rhi::ProgramSettings& settings) const final + { + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateComputeState(const Rhi::ComputeStateSettings& settings) const final + { + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateBuffer(const Rhi::BufferSettings& settings) const final + { + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateTexture(const Rhi::TextureSettings& settings) const final + { + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateSampler(const Rhi::SamplerSettings& settings) const final + { + return std::make_shared(*this, settings); + } }; } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Device.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Device.h index e3a873598..b72c14ef4 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Device.h +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Device.h @@ -36,6 +36,7 @@ class Device final // IDevice interface [[nodiscard]] Ptr CreateRenderContext(const Platform::AppEnvironment& env, tf::Executor& parallel_executor, const Rhi::RenderContextSettings& settings) override; + [[nodiscard]] Ptr CreateComputeContext(tf::Executor& parallel_executor, const Rhi::ComputeContextSettings& settings) override; }; } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Program.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Program.h index b824a8ff7..8fc7c17df 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Program.h +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Program.h @@ -23,6 +23,8 @@ Null implementation of the program interface. #pragma once +#include "Shader.h" + #include namespace Methane::Graphics::Null @@ -32,10 +34,12 @@ class Program final : public Base::Program { public: - using Base::Program::Program; + Program(const Base::Context& context, const Settings& settings); // IProgram interface [[nodiscard]] Ptr CreateBindings(const ResourceViewsByArgument& resource_views_by_argument, Data::Index frame_index) override; + + void SetArgumentBindings(const ResourceArgumentDescs& argument_descriptions); }; } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/RenderContext.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/RenderContext.h index f9dc478f6..9fa8caf87 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/RenderContext.h +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/RenderContext.h @@ -40,14 +40,6 @@ class RenderContext final // NOSONAR - this class requires destructor tf::Executor& parallel_executor, const Rhi::RenderContextSettings& settings); ~RenderContext() override; - // IContext interface - [[nodiscard]] Ptr CreateCommandQueue(Rhi::CommandListType type) const override; - [[nodiscard]] Ptr CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const override; - [[nodiscard]] Ptr CreateProgram(const Rhi::ProgramSettings& settings) const override; - [[nodiscard]] Ptr CreateBuffer(const Rhi::BufferSettings& settings) const override; - [[nodiscard]] Ptr CreateTexture(const Rhi::TextureSettings& settings) const override; - [[nodiscard]] Ptr CreateSampler(const Rhi::SamplerSettings& settings) const override; - // IRenderContext interface [[nodiscard]] Ptr CreateRenderState(const Rhi::RenderStateSettings& settings) const override; [[nodiscard]] Ptr CreateRenderPattern(const Rhi::RenderPatternSettings& settings) override; diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Resource.hpp b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Resource.hpp index e2be7fea1..03645db15 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Resource.hpp +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Resource.hpp @@ -27,12 +27,13 @@ Null implementation of the resource interface. #include #include +#include namespace Methane::Graphics::Null { template, void>> -class Resource +class Resource // NOSONAR - can not comply with rule of Zero: destructor is required : public ResourceBaseType , public virtual Rhi::IResource // NOSONAR { @@ -45,6 +46,22 @@ class Resource Resource(const Resource&) = delete; Resource(Resource&&) = delete; + ~Resource() override + { + META_FUNCTION_TASK(); + try + { + // Resource released callback has to be emitted before native resource is released + Data::Emitter::Emit(&Rhi::IResourceCallback::OnResourceReleased, std::ref(*this)); + } + catch(const std::exception& e) + { + META_UNUSED(e); + META_LOG("WARNING: Unexpected error during resource destruction: {}", e.what()); + assert(false); + } + } + bool operator=(const Resource&) = delete; bool operator=(Resource&&) = delete; diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Shader.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Shader.h index a01b69168..1870b8104 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Shader.h +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Shader.h @@ -25,9 +25,19 @@ Null implementation of the shader interface. #include +#include + namespace Methane::Graphics::Null { +struct ResourceArgumentDesc +{ + Rhi::ResourceType resource_type; + uint32_t resource_count; +}; + +using ResourceArgumentDescs = std::map; + class Shader final : public Base::Shader { @@ -36,6 +46,11 @@ class Shader final // Base::Shader interface Ptrs GetArgumentBindings(const Rhi::ProgramArgumentAccessors& argument_accessors) const override; + + void InitArgumentBindings(const ResourceArgumentDescs& argument_descriptions); + +private: + Ptrs m_argument_bindings; }; } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Texture.h b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Texture.h index 61e2e0d02..fd644cea6 100644 --- a/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Texture.h +++ b/Modules/Graphics/RHI/Null/Include/Methane/Graphics/Null/Texture.h @@ -38,6 +38,8 @@ class Texture final // NOSONAR - inheritance hierarchy is greater than 5 public: Texture(const Base::Context& context, const Settings& settings); Texture(const RenderContext& render_context, const Settings& settings, Data::Index frame_index); + + SubResource GetData(Rhi::ICommandQueue&, const SubResource::Index&, const BytesRangeOpt&) override; }; } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Buffer.cpp b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Buffer.cpp index aed3e3b1a..10ce1ce06 100644 --- a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Buffer.cpp +++ b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Buffer.cpp @@ -33,4 +33,9 @@ Buffer::Buffer(const Base::Context& context, const Settings& settings) { } +Rhi::SubResource Buffer::GetData(Rhi::ICommandQueue&, const BytesRangeOpt&) +{ + return {}; +} + } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/CommandQueue.cpp b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/CommandQueue.cpp index 3285ee31a..308377fd8 100644 --- a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/CommandQueue.cpp +++ b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/CommandQueue.cpp @@ -24,6 +24,7 @@ Null implementation of the command queue interface. #include #include #include +#include #include #include #include @@ -43,6 +44,12 @@ Ptr CommandQueue::CreateTransferCommandList() return std::make_shared(*this); } +Ptr CommandQueue::CreateComputeCommandList() +{ + META_FUNCTION_TASK(); + return std::make_shared(*this); +} + Ptr CommandQueue::CreateRenderCommandList(Rhi::IRenderPass& render_pass) { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/ComputeCommandList.cpp b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/ComputeCommandList.cpp new file mode 100644 index 000000000..87a51a117 --- /dev/null +++ b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/ComputeCommandList.cpp @@ -0,0 +1,41 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Null/ComputeCommandList.cpp +Null implementation of the compute command list interface. + +******************************************************************************/ + +#include "Methane/Graphics/Base/ComputeCommandList.h" +#include +#include + +namespace Methane::Graphics::Null +{ + +ComputeCommandList::ComputeCommandList(CommandQueue& command_queue) + : CommandList(command_queue) +{ } + +void ComputeCommandList::Dispatch(const Rhi::ThreadGroupsCount& thread_groups_count) +{ + m_dispatched_thread_groups_count = thread_groups_count; + Base::ComputeCommandList::Dispatch(thread_groups_count); +} + +} // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Device.cpp b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Device.cpp index cdc337d6e..3ee32f587 100644 --- a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Device.cpp +++ b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Device.cpp @@ -23,8 +23,7 @@ Null implementation of the buffer interface. #include #include - -#include +#include namespace Methane::Graphics::Null { @@ -36,4 +35,11 @@ Ptr Device::CreateRenderContext(const Platform::AppEnvironm return render_context_ptr; } +[[nodiscard]] Ptr Device::CreateComputeContext(tf::Executor& parallel_executor, const Rhi::ComputeContextSettings& settings) +{ + auto compute_context_ptr = std::make_shared(*this, parallel_executor, settings); + compute_context_ptr->Initialize(*this, true); + return compute_context_ptr; +} + } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Program.cpp b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Program.cpp index 345f5a8da..af5c06413 100644 --- a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Program.cpp +++ b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Program.cpp @@ -28,9 +28,23 @@ Null implementation of the program interface. namespace Methane::Graphics::Null { +Program::Program(const Base::Context& context, const Settings& settings) + : Base::Program(context, settings) +{ +} + Ptr Program::CreateBindings(const ResourceViewsByArgument& resource_views_by_argument, Data::Index frame_index) { return std::make_shared(*this, resource_views_by_argument, frame_index); } +void Program::SetArgumentBindings(const ResourceArgumentDescs& argument_descriptions) +{ + for(Rhi::ShaderType shader_type : GetShaderTypes()) + { + dynamic_cast(GetShaderRef(shader_type)).InitArgumentBindings(argument_descriptions); + } + Base::Program::InitArgumentBindings(GetSettings().argument_accessors); +} + } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/RenderContext.cpp b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/RenderContext.cpp index 804dcafa6..9c282fe8c 100644 --- a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/RenderContext.cpp +++ b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/RenderContext.cpp @@ -22,15 +22,9 @@ Null implementation of the render context interface. ******************************************************************************/ #include -#include -#include -#include #include #include #include -#include -#include -#include namespace Methane::Graphics::Null { @@ -54,42 +48,6 @@ RenderContext::~RenderContext() } } -Ptr RenderContext::CreateCommandQueue(Rhi::CommandListType type) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, type); -} - -Ptr RenderContext::CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(type, *this, settings); -} - -Ptr RenderContext::CreateProgram(const Rhi::ProgramSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateBuffer(const Rhi::BufferSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateTexture(const Rhi::TextureSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateSampler(const Rhi::SamplerSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - Ptr RenderContext::CreateRenderState(const Rhi::RenderStateSettings& settings) const { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Shader.cpp b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Shader.cpp index 558581105..bd30d0b0d 100644 --- a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Shader.cpp +++ b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Shader.cpp @@ -21,7 +21,10 @@ Null implementation of the shader interface. ******************************************************************************/ +#include "Methane/Graphics/Base/ProgramArgumentBinding.h" +#include "Methane/Graphics/RHI/IProgram.h" #include +#include #include @@ -30,7 +33,28 @@ namespace Methane::Graphics::Null Ptrs Shader::GetArgumentBindings(const Rhi::ProgramArgumentAccessors&) const { - return {}; + return m_argument_bindings; +} + +void Shader::InitArgumentBindings(const ResourceArgumentDescs& argument_descriptions) +{ + m_argument_bindings.clear(); + m_argument_bindings.reserve(argument_descriptions.size()); + for(const auto& [argument_accessor, argument_desc] : argument_descriptions) + { + if (argument_accessor.GetShaderType() != GetType()) + continue; + + auto argument_binding_ptr = std::make_shared(GetContext(), + Rhi::ProgramArgumentBindingSettings + { + argument_accessor, + argument_desc.resource_type, + argument_desc.resource_count + }); + + m_argument_bindings.push_back(std::static_pointer_cast(argument_binding_ptr)); + } } } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Texture.cpp b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Texture.cpp index 5d18b8502..0c5456f61 100644 --- a/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Texture.cpp +++ b/Modules/Graphics/RHI/Null/Sources/Methane/Graphics/Null/Texture.cpp @@ -24,7 +24,6 @@ Null implementation of the texture interface. #include #include -#include #include namespace Methane::Graphics::Null @@ -42,4 +41,9 @@ Texture::Texture(const RenderContext& render_context, const Settings& settings, META_CHECK_ARG_EQUAL(frame_index, settings.frame_index_opt.value()); } +Rhi::SubResource Texture::GetData(Rhi::ICommandQueue&, const SubResource::Index&, const BytesRangeOpt&) +{ + return {}; +} + } // namespace Methane::Graphics::Null diff --git a/Modules/Graphics/RHI/Vulkan/CMakeLists.txt b/Modules/Graphics/RHI/Vulkan/CMakeLists.txt index e5938fa5a..136d9190f 100644 --- a/Modules/Graphics/RHI/Vulkan/CMakeLists.txt +++ b/Modules/Graphics/RHI/Vulkan/CMakeLists.txt @@ -17,8 +17,10 @@ list(APPEND HEADERS ${INCLUDE_DIR}/ProgramArgumentBinding.h ${INCLUDE_DIR}/ProgramBindings.h ${INCLUDE_DIR}/RenderContext.h + ${INCLUDE_DIR}/ComputeContext.h ${INCLUDE_DIR}/RenderState.h ${INCLUDE_DIR}/ViewState.h + ${INCLUDE_DIR}/ComputeState.h ${INCLUDE_DIR}/IResource.h ${INCLUDE_DIR}/ResourceView.h ${INCLUDE_DIR}/ResourceBarriers.h @@ -37,6 +39,7 @@ list(APPEND HEADERS ${INCLUDE_DIR}/ICommandList.h ${INCLUDE_DIR}/CommandList.hpp ${INCLUDE_DIR}/TransferCommandList.h + ${INCLUDE_DIR}/ComputeCommandList.h ${INCLUDE_DIR}/RenderCommandList.h ${INCLUDE_DIR}/ParallelRenderCommandList.h ${INCLUDE_DIR}/Utils.hpp @@ -56,6 +59,7 @@ list(APPEND SOURCES ${SOURCES_DIR}/RenderContext.cpp ${SOURCES_DIR}/RenderState.cpp ${SOURCES_DIR}/ViewState.cpp + ${SOURCES_DIR}/ComputeState.cpp ${SOURCES_DIR}/IResource.cpp ${SOURCES_DIR}/ResourceView.cpp ${SOURCES_DIR}/ResourceBarriers.cpp @@ -71,6 +75,7 @@ list(APPEND SOURCES ${SOURCES_DIR}/CommandListSet.cpp ${SOURCES_DIR}/CommandListDebugGroup.cpp ${SOURCES_DIR}/TransferCommandList.cpp + ${SOURCES_DIR}/ComputeCommandList.cpp ${SOURCES_DIR}/RenderCommandList.cpp ${SOURCES_DIR}/ParallelRenderCommandList.cpp ) diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Buffer.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Buffer.h index a74fcf96a..dbd0b6b9a 100644 --- a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Buffer.h +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Buffer.h @@ -39,8 +39,9 @@ class Buffer final // NOSONAR - inheritance hierarchy is greater than 5 public: Buffer(const Base::Context& context, const Settings& settings); - // IResource interface - void SetData(const SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) override; + // IBuffer interface + void SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResource& sub_resource) override; + SubResource GetData(Rhi::ICommandQueue& target_cmd_queue, const BytesRangeOpt& data_range = {}) override; // IObject interface bool SetName(std::string_view name) override; @@ -50,9 +51,12 @@ class Buffer final // NOSONAR - inheritance hierarchy is greater than 5 Ptr CreateNativeViewDescriptor(const View::Id& view_id) override; private: - vk::UniqueBuffer m_vk_unique_staging_buffer; - vk::UniqueDeviceMemory m_vk_unique_staging_memory; - std::vector m_vk_copy_regions; + Data::Bytes GetDataFromSharedBuffer(const BytesRange& data_range) const; + Data::Bytes GetDataFromPrivateBuffer(const BytesRange& data_range, Rhi::ICommandQueue& target_cmd_queue); + + vk::UniqueBuffer m_vk_unique_staging_buffer; + vk::UniqueDeviceMemory m_vk_unique_staging_memory; + vk::BufferCopy m_vk_copy_region; }; } // namespace Methane::Graphics::Vulkan diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/CommandListSet.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/CommandListSet.h index 8776b3c3b..c1e8094cf 100644 --- a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/CommandListSet.h +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/CommandListSet.h @@ -24,8 +24,8 @@ Vulkan command list set implementation. #pragma once #include +#include -#include #include #include diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/CommandQueue.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/CommandQueue.h index fbbaa949e..f2a78177f 100644 --- a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/CommandQueue.h +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/CommandQueue.h @@ -24,10 +24,9 @@ Vulkan implementation of the command queue interface. #pragma once #include +#include -#include #include - #include namespace Methane::Graphics::Vulkan @@ -55,6 +54,7 @@ class CommandQueue final // NOSONAR - custom destructor is required // ICommandQueue interface [[nodiscard]] Ptr CreateFence() override; [[nodiscard]] Ptr CreateTransferCommandList() override; + [[nodiscard]] Ptr CreateComputeCommandList() override; [[nodiscard]] Ptr CreateRenderCommandList(Rhi::IRenderPass& render_pass) override; [[nodiscard]] Ptr CreateParallelRenderCommandList(Rhi::IRenderPass& render_pass) override; [[nodiscard]] Ptr CreateTimestampQueryPool(uint32_t max_timestamps_per_frame) override; diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/ComputeCommandList.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/ComputeCommandList.h new file mode 100644 index 000000000..8e9d13970 --- /dev/null +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/ComputeCommandList.h @@ -0,0 +1,47 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Vulkan/ComputeCommandList.h +Vulkan implementation of the compute command list interface. + +******************************************************************************/ + +#pragma once + +#include "CommandList.hpp" + +#include + +#include + +namespace Methane::Graphics::Vulkan +{ + +class CommandQueue; + +class ComputeCommandList final // NOSONAR - inheritance hierarchy depth is greater than 5 + : public CommandList +{ +public: + explicit ComputeCommandList(CommandQueue& command_queue); + + // IComputeCommandList interface + void Dispatch(const Rhi::ThreadGroupsCount& thread_groups_count) override; +}; + +} // namespace Methane::Graphics::Vulkan diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/ComputeContext.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/ComputeContext.h new file mode 100644 index 000000000..7833344f8 --- /dev/null +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/ComputeContext.h @@ -0,0 +1,40 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Vulkan/ComputeContext.hh +Vulkan implementation of the compute context interface. + +******************************************************************************/ + +#pragma once + +#include "Context.hpp" + +#include + +namespace Methane::Graphics::Vulkan +{ + +class ComputeContext final // NOSONAR - inheritance hierarchy depth is greater than 5 + : public Context +{ +public: + using Context::Context; +}; + +} // namespace Methane::Graphics::Vulkan diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/ComputeState.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/ComputeState.h new file mode 100644 index 000000000..2f76ababe --- /dev/null +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/ComputeState.h @@ -0,0 +1,62 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Vulkan/ComputeState.h +Vulkan implementation of the compute state interface. + +******************************************************************************/ + +#pragma once + +#include + +#include + +namespace Methane::Graphics::Vulkan +{ + +struct IContext; +class Device; + +class ComputeState final + : public Base::ComputeState +{ +public: + ComputeState(const Rhi::IContext& context, const Settings& settings); + + // IComputeState interface + void Reset(const Settings& settings) override; + + // Base::ComputeState interface + void Apply(Base::ComputeCommandList& compute_command_list) override; + + // IObject interface + bool SetName(std::string_view name) override; + + const vk::Pipeline& GetNativePipeline() const noexcept + { + return m_vk_unique_pipeline.get(); + } + +private: + const Device& m_device; + const IContext& m_vk_context; + vk::UniquePipeline m_vk_unique_pipeline; +}; + +} // namespace Methane::Graphics::Vulkan diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Context.hpp b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Context.hpp index 9605e1248..768730b82 100644 --- a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Context.hpp +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Context.hpp @@ -26,6 +26,12 @@ Vulkan template implementation of the base context interface. #include "IContext.h" #include "Device.h" #include "CommandQueue.h" +#include "Shader.h" +#include "Program.h" +#include "ComputeState.h" +#include "Buffer.h" +#include "Texture.h" +#include "Sampler.h" #include "DescriptorManager.h" #include @@ -35,13 +41,6 @@ Vulkan template implementation of the base context interface. #include #include -namespace Methane::Graphics::Rhi -{ - -struct ICommandQueue; - -} // namespace Methane::Graphics::Rhi - namespace Methane::Graphics::Vulkan { @@ -66,7 +65,49 @@ class Context ContextBaseT::Release(); } - // IContext interface + // IContext overrides + + [[nodiscard]] Ptr CreateCommandQueue(Rhi::CommandListType type) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, type); + } + + [[nodiscard]] Ptr CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(type, *this, settings); + } + + [[nodiscard]] Ptr CreateProgram(const Rhi::ProgramSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateComputeState(const Rhi::ComputeStateSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateBuffer(const Rhi::BufferSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateTexture(const Rhi::TextureSettings& settings) const override + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } + + [[nodiscard]] Ptr CreateSampler(const Rhi::SamplerSettings& settings) const final + { + META_FUNCTION_TASK(); + return std::make_shared(*this, settings); + } const Device& GetVulkanDevice() const noexcept final { diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/DescriptorManager.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/DescriptorManager.h index 70b2889f9..f3b8e96d4 100644 --- a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/DescriptorManager.h +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/DescriptorManager.h @@ -24,17 +24,13 @@ Vulkan descriptor manager with descriptor sets allocator. #pragma once #include +#include -#include #include - #include #include #include -// Uncomment to enable deferred program bindings initialization -#define DEFERRED_PROGRAM_BINDINGS_INITIALIZATION - namespace Methane::Graphics::Rhi { @@ -76,9 +72,6 @@ class DescriptorManager final }); // IDescriptorManager overrides -#ifndef DEFERRED_PROGRAM_BINDINGS_INITIALIZATION - void CompleteInitialization() override { /* intentionally uninitialized */} -#endif void Release() override; void SetDescriptorPoolSizeRatio(vk::DescriptorType descriptor_type, float size_ratio); diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Device.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Device.h index 37cf0af16..1c6167ec3 100644 --- a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Device.h +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Device.h @@ -86,6 +86,7 @@ class Device final // IDevice interface [[nodiscard]] Ptr CreateRenderContext(const Methane::Platform::AppEnvironment& env, tf::Executor& parallel_executor, const Rhi::RenderContextSettings& settings) override; + [[nodiscard]] Ptr CreateComputeContext(tf::Executor& parallel_executor, const Rhi::ComputeContextSettings& settings) override; // IObject interface bool SetName(std::string_view name) override; diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/RenderContext.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/RenderContext.h index 145df49e3..f56538720 100644 --- a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/RenderContext.h +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/RenderContext.h @@ -61,15 +61,10 @@ class RenderContext final // NOSONAR - this class requires destructor ~RenderContext() override; // IContext interface - [[nodiscard]] Ptr CreateCommandQueue(Rhi::CommandListType type) const override; - [[nodiscard]] Ptr CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const override; - [[nodiscard]] Ptr CreateProgram(const Rhi::ProgramSettings& settings) const override; - [[nodiscard]] Ptr CreateBuffer(const Rhi::BufferSettings& settings) const override; - [[nodiscard]] Ptr CreateTexture(const Rhi::TextureSettings& settings) const override; - [[nodiscard]] Ptr CreateSampler(const Rhi::SamplerSettings& settings) const override; void WaitForGpu(WaitFor wait_for) override; // IRenderContext interface + [[nodiscard]] Ptr CreateTexture(const Rhi::TextureSettings& settings) const override; [[nodiscard]] Ptr CreateRenderState(const Rhi::RenderStateSettings& settings) const override; [[nodiscard]] Ptr CreateRenderPattern(const Rhi::RenderPatternSettings& settings) override; bool ReadyToRender() const override; diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Resource.hpp b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Resource.hpp index d2af90f8d..688ab24b9 100644 --- a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Resource.hpp +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Resource.hpp @@ -206,7 +206,7 @@ class Resource // NOSONAR - destructor in use m_vk_resource = std::forward(vk_resource); } - TransferCommandList& PrepareResourceUpload(Rhi::ICommandQueue& target_cmd_queue) + TransferCommandList& PrepareResourceTransfer(Rhi::ICommandQueue& target_cmd_queue, State transfer_state) { META_FUNCTION_TASK(); const Rhi::ICommandKit& upload_cmd_kit = Base::Resource::GetContext().GetUploadCommandKit(); @@ -214,7 +214,7 @@ class Resource // NOSONAR - destructor in use upload_cmd_list.RetainResource(*this); const bool owner_changed = SetOwnerQueueFamily(upload_cmd_kit.GetQueue().GetFamilyIndex(), m_upload_begin_transition_barriers_ptr); - if (const bool state_changed = SetState(State::CopyDest, m_upload_begin_transition_barriers_ptr); + if (const bool state_changed = SetState(transfer_state, m_upload_begin_transition_barriers_ptr); (owner_changed || state_changed) && m_upload_begin_transition_barriers_ptr && !m_upload_begin_transition_barriers_ptr->IsEmpty()) { @@ -232,7 +232,7 @@ class Resource // NOSONAR - destructor in use return upload_cmd_list; } - void CompleteResourceUpload(TransferCommandList& upload_cmd_list, State final_resource_state, Rhi::ICommandQueue& target_cmd_queue) + void CompleteResourceTransfer(TransferCommandList& upload_cmd_list, State final_resource_state, Rhi::ICommandQueue& target_cmd_queue) { META_FUNCTION_TASK(); const bool owner_changed = SetOwnerQueueFamily(target_cmd_queue.GetFamilyIndex(), m_upload_end_transition_barriers_ptr); diff --git a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Texture.h b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Texture.h index e9c37eafb..f81226480 100644 --- a/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Texture.h +++ b/Modules/Graphics/RHI/Vulkan/Include/Methane/Graphics/Vulkan/Texture.h @@ -49,15 +49,18 @@ class Texture final // NOSONAR - inheritance hierarchy is greater than 5 void ResetNativeFrameImage(); - // IResource interface - void SetData(const SubResources& sub_resources, Rhi::ICommandQueue&) override; + // ITexture interface + void SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResources& sub_resources) override; + SubResource GetData(Rhi::ICommandQueue& target_cmd_queue, + const SubResource::Index& sub_resource_index = {}, + const BytesRangeOpt& data_range = {}) override; // IObject overide bool SetName(std::string_view name) override; // ITexture overrides const vk::Image& GetNativeImage() const noexcept { return GetNativeResource(); } - vk::ImageSubresourceRange GetNativeSubresourceRange() const noexcept; + vk::ImageSubresourceRange GetNativeSubresourceRange() const; private: Texture(const Base::Context& context, const Settings& settings, vk::UniqueImage&& vk_unique_image); diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Buffer.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Buffer.cpp index 00ee4f1a3..05d87f4f2 100644 --- a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Buffer.cpp +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Buffer.cpp @@ -100,51 +100,103 @@ Buffer::Buffer(const Base::Context& context, const Settings& settings) GetNativeDevice().bindBufferMemory(m_vk_unique_staging_buffer.get(), m_vk_unique_staging_memory.get(), 0); } -void Buffer::SetData(const Rhi::SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) +void Buffer::SetData(Rhi::ICommandQueue& target_cmd_queue, const Rhi::SubResource& sub_resource) { META_FUNCTION_TASK(); - Resource::SetData(sub_resources, target_cmd_queue); + Base::Buffer::SetData(target_cmd_queue, sub_resource); const Settings& buffer_settings = GetSettings(); const bool is_private_storage = buffer_settings.storage_mode == Rhi::IBuffer::StorageMode::Private; - if (is_private_storage) - { - m_vk_copy_regions.clear(); - m_vk_copy_regions.reserve(sub_resources.size()); - } - const vk::DeviceMemory& vk_device_memory = is_private_storage ? m_vk_unique_staging_memory.get() : GetNativeDeviceMemory(); - for(const SubResource& sub_resource : sub_resources) - { - ValidateSubResource(sub_resource); - const vk::DeviceSize sub_resource_offset = 0U; - Data::RawPtr sub_resource_data_ptr = nullptr; - const vk::Result vk_map_result = GetNativeDevice().mapMemory(vk_device_memory, sub_resource_offset, sub_resource.GetDataSize(), vk::MemoryMapFlags{}, - reinterpret_cast(&sub_resource_data_ptr)); // NOSONAR + const vk::DeviceSize sub_resource_offset = 0U; + Data::RawPtr sub_resource_data_ptr = nullptr; + const vk::Result vk_map_result = GetNativeDevice().mapMemory(vk_device_memory, sub_resource_offset, sub_resource.GetDataSize(), vk::MemoryMapFlags{}, + reinterpret_cast(&sub_resource_data_ptr)); // NOSONAR - META_CHECK_ARG_EQUAL_DESCR(vk_map_result, vk::Result::eSuccess, "failed to map buffer subresource"); - META_CHECK_ARG_NOT_NULL_DESCR(sub_resource_data_ptr, "failed to map buffer subresource"); - std::copy(sub_resource.GetDataPtr(), sub_resource.GetDataEndPtr(), sub_resource_data_ptr); + META_CHECK_ARG_EQUAL_DESCR(vk_map_result, vk::Result::eSuccess, "failed to map buffer subresource"); + META_CHECK_ARG_NOT_NULL_DESCR(sub_resource_data_ptr, "failed to map buffer subresource"); + std::copy(sub_resource.GetDataPtr(), sub_resource.GetDataEndPtr(), sub_resource_data_ptr); - GetNativeDevice().unmapMemory(vk_device_memory); + GetNativeDevice().unmapMemory(vk_device_memory); - if (is_private_storage) - { - m_vk_copy_regions.emplace_back(sub_resource_offset, sub_resource_offset, static_cast(sub_resource.GetDataSize())); - } + if (is_private_storage) + { + m_vk_copy_region = vk::BufferCopy(sub_resource_offset, sub_resource_offset, static_cast(sub_resource.GetDataSize())); } if (!is_private_storage) return; // In case of private GPU storage, copy buffer data from staging upload resource to the device-local GPU resource - TransferCommandList& upload_cmd_list = PrepareResourceUpload(target_cmd_queue); - upload_cmd_list.GetNativeCommandBufferDefault().copyBuffer(m_vk_unique_staging_buffer.get(), GetNativeResource(), m_vk_copy_regions); - CompleteResourceUpload(upload_cmd_list, GetTargetResourceStateByBufferType(buffer_settings.type), target_cmd_queue); + TransferCommandList& upload_cmd_list = PrepareResourceTransfer(target_cmd_queue, State::CopyDest); + upload_cmd_list.GetNativeCommandBufferDefault().copyBuffer(m_vk_unique_staging_buffer.get(), GetNativeResource(), 1U, &m_vk_copy_region); + CompleteResourceTransfer(upload_cmd_list, GetTargetResourceStateByBufferType(buffer_settings.type), target_cmd_queue); GetContext().RequestDeferredAction(Rhi::ContextDeferredAction::UploadResources); } +Rhi::SubResource Buffer::GetData(Rhi::ICommandQueue& target_cmd_queue, const BytesRangeOpt& data_range) +{ + META_FUNCTION_TASK(); + META_CHECK_ARG_TRUE_DESCR(GetUsage().HasAnyBit(Rhi::ResourceUsage::ReadBack), + "getting buffer data from GPU is allowed for buffers with CPU Read-back flag only"); + + const BytesRange buffer_data_range(data_range ? data_range->GetStart() : 0U, + data_range ? data_range->GetEnd() : GetDataSize()); + + Data::Bytes data; + switch(GetSettings().storage_mode) + { + case IBuffer::StorageMode::Managed: data = GetDataFromSharedBuffer(buffer_data_range); break; + case IBuffer::StorageMode::Private: data = GetDataFromPrivateBuffer(buffer_data_range, target_cmd_queue); break; + default: META_UNEXPECTED_ARG_RETURN(GetSettings().storage_mode, SubResource()); + } + + return Rhi::SubResource(std::move(data), Rhi::SubResourceIndex(), data_range); +} + +Data::Bytes Buffer::GetDataFromSharedBuffer(const BytesRange& data_range) const +{ + META_FUNCTION_TASK(); + Data::RawPtr data_ptr = nullptr; + const vk::DeviceMemory& vk_device_memory = GetNativeDeviceMemory(); + const vk::Result vk_map_result = GetNativeDevice().mapMemory(vk_device_memory, data_range.GetStart(), data_range.GetLength(), + vk::MemoryMapFlags{}, reinterpret_cast(&data_ptr)); // NOSONAR + + META_CHECK_ARG_EQUAL_DESCR(vk_map_result, vk::Result::eSuccess, "failed to map buffer subresource"); + META_CHECK_ARG_NOT_NULL_DESCR(data_ptr, "failed to map buffer subresource"); + Data::Bytes data(data_ptr, data_ptr + data_range.GetLength()); + GetNativeDevice().unmapMemory(vk_device_memory); + + return data; +} + +Data::Bytes Buffer::GetDataFromPrivateBuffer(const BytesRange& data_range, Rhi::ICommandQueue& target_cmd_queue) +{ + META_FUNCTION_TASK(); + const State initial_buffer_state = GetState(); + TransferCommandList& upload_cmd_list = PrepareResourceTransfer(target_cmd_queue, State::CopySource); + const vk::CommandBuffer& vk_cmd_buffer = upload_cmd_list.GetNativeCommandBufferDefault(); + const vk::BufferCopy vk_buffer_copy(data_range.GetStart(), 0U, data_range.GetLength()); + vk_cmd_buffer.copyBuffer(GetNativeResource(), m_vk_unique_staging_buffer.get(), 1U, &vk_buffer_copy); + + CompleteResourceTransfer(upload_cmd_list, initial_buffer_state, target_cmd_queue); + + // Execute resource transfer commands and wait for completion + GetBaseContext().UploadResources(); + + // Copy buffer data from mapped staging resource + Data::RawPtr data_ptr = nullptr; + const vk::Result vk_map_result = GetNativeDevice().mapMemory(m_vk_unique_staging_memory.get(), 0U, data_range.GetLength(), + vk::MemoryMapFlags{}, reinterpret_cast(&data_ptr)); // NOSONAR + META_CHECK_ARG_EQUAL_DESCR(vk_map_result, vk::Result::eSuccess, "failed to map buffer subresource"); + META_CHECK_ARG_NOT_NULL_DESCR(data_ptr, "failed to map buffer subresource"); + Data::Bytes data(data_ptr, data_ptr + data_range.GetLength()); + GetNativeDevice().unmapMemory(m_vk_unique_staging_memory.get()); + + return data; +} + bool Buffer::SetName(std::string_view name) { META_FUNCTION_TASK(); @@ -165,7 +217,7 @@ Ptr Buffer::CreateNativeViewDescriptor(cons buffer_view_desc.vk_desc = vk::DescriptorBufferInfo( GetNativeResource(), static_cast(view_id.offset), - view_id.size ? view_id.size : GetSubResourceDataSize(view_id.subresource_index) + view_id.size ? view_id.size : GetDataSize() ); return std::make_shared(std::move(buffer_view_desc)); diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/CommandQueue.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/CommandQueue.cpp index 5ab46c2f5..c5f7ce1aa 100644 --- a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/CommandQueue.cpp +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/CommandQueue.cpp @@ -25,6 +25,7 @@ Vulkan implementation of the command queue interface. #include #include #include +#include #include #include #include @@ -145,6 +146,12 @@ Ptr CommandQueue::CreateTransferCommandList() return std::make_shared(*this); } +Ptr CommandQueue::CreateComputeCommandList() +{ + META_FUNCTION_TASK(); + return std::make_shared(*this); +} + Ptr CommandQueue::CreateRenderCommandList(Rhi::IRenderPass& render_pass) { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ComputeCommandList.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ComputeCommandList.cpp new file mode 100644 index 000000000..e32b9064b --- /dev/null +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ComputeCommandList.cpp @@ -0,0 +1,44 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Vulkan/ComputeCommandList.cpp +Vulkan implementation of the compute command list interface. + +******************************************************************************/ + +#include +#include + +#include +#include + +namespace Methane::Graphics::Vulkan +{ + +ComputeCommandList::ComputeCommandList(CommandQueue& command_queue) + : CommandList(vk::CommandBufferLevel::ePrimary, {}, command_queue) +{ } + +void ComputeCommandList::Dispatch(const Rhi::ThreadGroupsCount& thread_groups_count) +{ + META_FUNCTION_TASK(); + Base::ComputeCommandList::Dispatch(thread_groups_count); + GetNativeCommandBufferDefault().dispatch(thread_groups_count.GetWidth(), thread_groups_count.GetHeight(), thread_groups_count.GetDepth()); +} + +} // namespace Methane::Graphics::Vulkan diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ComputeState.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ComputeState.cpp new file mode 100644 index 000000000..472098d2a --- /dev/null +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ComputeState.cpp @@ -0,0 +1,100 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Methane/Graphics/Vulkan/ComputeState.cpp +Vulkan implementation of the compute state interface. + +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace Methane::Graphics::Vulkan +{ + +static const Device& GetVulkanDeviceFromContext(const Rhi::IContext& context) +{ + META_FUNCTION_TASK(); + switch (context.GetType()) + { + case Rhi::ContextType::Render: return dynamic_cast(context).GetVulkanDevice(); + case Rhi::ContextType::Compute: return dynamic_cast(context).GetVulkanDevice(); + } + return dynamic_cast(context).GetVulkanDevice(); +} + +ComputeState::ComputeState(const Rhi::IContext& context, const Settings& settings) + : Base::ComputeState(context, settings) + , m_device(GetVulkanDeviceFromContext(context)) + , m_vk_context(dynamic_cast(context)) +{ + META_FUNCTION_TASK(); + Reset(settings); +} + +void ComputeState::Reset(const Settings& settings) +{ + META_FUNCTION_TASK(); + Base::ComputeState::Reset(settings); + + auto& program = static_cast(*GetSettings().program_ptr); + const std::vector vk_stages_info = program.GetNativeShaderStageCreateInfos(); + + const vk::ComputePipelineCreateInfo vk_pipeline_create_info( + vk::PipelineCreateFlags(), + vk_stages_info.back(), + program.GetNativePipelineLayout() + ); + + auto pipe = m_vk_context.GetVulkanDevice().GetNativeDevice().createComputePipelineUnique(nullptr, vk_pipeline_create_info); + META_CHECK_ARG_EQUAL_DESCR(pipe.result, vk::Result::eSuccess, "Vulkan pipeline creation has failed"); + m_vk_unique_pipeline = std::move(pipe.value); +} + +void ComputeState::Apply(Base::ComputeCommandList& compute_command_list) +{ + META_FUNCTION_TASK(); + const auto& vulkan_compute_command_list = static_cast(compute_command_list); + vulkan_compute_command_list.GetNativeCommandBufferDefault().bindPipeline(vk::PipelineBindPoint::eCompute, GetNativePipeline()); +} + +bool ComputeState::SetName(std::string_view name) +{ + META_FUNCTION_TASK(); + if (!Base::ComputeState::SetName(name)) + return false; + + SetVulkanObjectName(m_vk_context.GetVulkanDevice().GetNativeDevice(), m_vk_unique_pipeline.get(), name); + return true; +} + +} // namespace Methane::Graphics::Vulkan diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Device.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Device.cpp index 5cc3702ee..89085c5e6 100644 --- a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Device.cpp +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Device.cpp @@ -24,6 +24,7 @@ Vulkan implementation of the device interface. #include #include #include +#include #include #include @@ -146,8 +147,9 @@ static vk::QueueFlags GetQueueFlagsByType(Rhi::CommandListType cmd_list_type) META_FUNCTION_TASK(); switch(cmd_list_type) { - case Rhi::CommandListType::Transfer: return vk::QueueFlagBits::eTransfer; - case Rhi::CommandListType::Render: return vk::QueueFlagBits::eGraphics; + case Rhi::CommandListType::Transfer: return vk::QueueFlagBits::eTransfer; + case Rhi::CommandListType::Render: return vk::QueueFlagBits::eGraphics; + case Rhi::CommandListType::Compute: return vk::QueueFlagBits::eCompute; default: META_UNEXPECTED_ARG_RETURN(cmd_list_type, vk::QueueFlagBits::eGraphics); } } @@ -220,11 +222,10 @@ Rhi::DeviceFeatureMask Device::GetSupportedFeatures(const vk::PhysicalDevice& vk { META_FUNCTION_TASK(); vk::PhysicalDeviceFeatures vk_device_features = vk_physical_device.getFeatures(); - Rhi::DeviceFeatureMask device_features; - device_features.SetBit(Rhi::DeviceFeature::PresentToWindow, IsDeviceExtensionSupported(vk_physical_device, g_present_device_extensions)); + device_features.SetBit(Rhi::DeviceFeature::PresentToWindow, IsDeviceExtensionSupported(vk_physical_device, g_present_device_extensions)); device_features.SetBit(Rhi::DeviceFeature::AnisotropicFiltering, vk_device_features.samplerAnisotropy); - device_features.SetBit(Rhi::DeviceFeature::ImageCubeArray, vk_device_features.imageCubeArray); + device_features.SetBit(Rhi::DeviceFeature::ImageCubeArray, vk_device_features.imageCubeArray); return device_features; } @@ -237,15 +238,15 @@ Device::Device(const vk::PhysicalDevice& vk_physical_device, const vk::SurfaceKH { META_FUNCTION_TASK(); if (const Rhi::DeviceFeatureMask device_supported_features = Device::GetSupportedFeatures(vk_physical_device); - !static_cast(device_supported_features & capabilities.features)) + !device_supported_features.HasBits(capabilities.features)) throw IncompatibleException("Supported Device features are incompatible with the required capabilities"); std::vector reserved_queues_count_per_family(m_vk_queue_family_properties.size(), 0U); - ReserveQueueFamily(Rhi::CommandListType::Render, capabilities.render_queues_count, reserved_queues_count_per_family, + ReserveQueueFamily(Rhi::CommandListType::Render, capabilities.render_queues_count, reserved_queues_count_per_family, capabilities.features.HasBit(Rhi::DeviceFeature::PresentToWindow) ? vk_surface : vk::SurfaceKHR()); - ReserveQueueFamily(Rhi::CommandListType::Transfer, capabilities.transfer_queues_count, reserved_queues_count_per_family); + ReserveQueueFamily(Rhi::CommandListType::Compute, capabilities.compute_queues_count, reserved_queues_count_per_family); std::vector vk_queue_create_infos; std::set unique_family_reservation_ptrs; @@ -259,12 +260,19 @@ Device::Device(const vk::PhysicalDevice& vk_physical_device, const vk::SurfaceKH } std::vector enabled_extension_names = g_common_device_extensions; - if (capabilities.features.HasBit(Rhi::DeviceFeature::PresentToWindow)) + if (capabilities.render_queues_count) { - enabled_extension_names.insert(enabled_extension_names.end(), g_present_device_extensions.begin(), g_present_device_extensions.end()); + if (capabilities.features.HasBit(Rhi::DeviceFeature::PresentToWindow)) + { + enabled_extension_names.insert(enabled_extension_names.end(), g_present_device_extensions.begin(), g_present_device_extensions.end()); + } + enabled_extension_names.insert(enabled_extension_names.end(), g_render_device_extensions.begin(), g_render_device_extensions.end()); } - enabled_extension_names.insert(enabled_extension_names.end(), g_render_device_extensions.begin(), g_render_device_extensions.end()); + if (IsExtensionSupported({ "VK_KHR_portability_subset" })) + { + enabled_extension_names.emplace_back("VK_KHR_portability_subset"); + } std::vector raw_enabled_extension_names; std::transform(enabled_extension_names.begin(), enabled_extension_names.end(), std::back_inserter(raw_enabled_extension_names), @@ -301,6 +309,14 @@ Ptr Device::CreateRenderContext(const Methane::Platform::Ap return render_context_ptr; } +Ptr Device::CreateComputeContext(tf::Executor& parallel_executor, const Rhi::ComputeContextSettings& settings) +{ + META_FUNCTION_TASK(); + const auto compute_context_ptr = std::make_shared(*this, parallel_executor, settings); + compute_context_ptr->Initialize(*this, true); + return compute_context_ptr; +} + bool Device::SetName(std::string_view name) { META_FUNCTION_TASK(); diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/IResource.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/IResource.cpp index 514842323..58781470c 100644 --- a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/IResource.cpp +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/IResource.cpp @@ -110,7 +110,8 @@ vk::PipelineStageFlags IResource::GetNativePipelineStageFlagsByResourceState(Rhi case Rhi::ResourceState::UnorderedAccess: case Rhi::ResourceState::ShaderResource: return vk::PipelineStageFlagBits::eVertexShader | // All possible shader stages - vk::PipelineStageFlagBits::eFragmentShader; + vk::PipelineStageFlagBits::eFragmentShader | + vk::PipelineStageFlagBits::eComputeShader; case Rhi::ResourceState::CopyDest: case Rhi::ResourceState::CopySource: case Rhi::ResourceState::ResolveDest: diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ProgramArgumentBinding.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ProgramArgumentBinding.cpp index 7b27716b2..c7d16f27e 100644 --- a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ProgramArgumentBinding.cpp +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ProgramArgumentBinding.cpp @@ -30,6 +30,20 @@ Vulkan implementation of the program argument binding interface. namespace Methane::Graphics::Vulkan { +static Rhi::ResourceUsageMask GetResourceUsageByDescriptorType(vk::DescriptorType descriptor_type) +{ + META_FUNCTION_TASK(); + Rhi::ResourceUsageMask resource_usage{ Rhi::ResourceUsage::ShaderRead }; + if (descriptor_type == vk::DescriptorType::eStorageImage || + descriptor_type == vk::DescriptorType::eStorageTexelBuffer || + descriptor_type == vk::DescriptorType::eStorageBuffer || + descriptor_type == vk::DescriptorType::eStorageBufferDynamic) + { + resource_usage.SetBitOn(Rhi::ResourceUsage::ShaderWrite); + } + return resource_usage; +} + ProgramArgumentBinding::ProgramArgumentBinding(const Base::Context& context, const Settings& settings) : Base::ProgramArgumentBinding(context, settings) , m_settings_vk(settings) @@ -91,9 +105,11 @@ bool ProgramArgumentBinding::SetResourceViews(const Rhi::IResource::Views& resou m_vk_buffer_views.clear(); const size_t total_resources_count = resource_views.size(); - for(const Rhi::IResource::View& resource_view : resource_views) + const Rhi::ResourceUsageMask resource_usage = GetResourceUsageByDescriptorType(m_settings_vk.descriptor_type); + + for(const Rhi::ResourceView& resource_view : resource_views) { - const IResource::View resource_view_vk(resource_view, Rhi::ResourceUsageMask(Rhi::ResourceUsage::ShaderRead)); + const ResourceView resource_view_vk(resource_view, resource_usage); if (AddDescriptor(m_vk_descriptor_images, total_resources_count, resource_view_vk.GetNativeDescriptorImageInfoPtr())) continue; @@ -115,11 +131,14 @@ bool ProgramArgumentBinding::SetResourceViews(const Rhi::IResource::Views& resou ); // Descriptions are updated on GPU during context initialization complete -#ifdef DEFERRED_PROGRAM_BINDINGS_INITIALIZATION - GetContext().RequestDeferredAction(Rhi::IContext::DeferredAction::CompleteInitialization); -#else - UpdateDescriptorSetsOnGpu(); -#endif + if (GetContext().GetOptions().HasBit(Rhi::ContextOption::DeferredProgramBindingsInitialization)) + { + GetContext().RequestDeferredAction(Rhi::IContext::DeferredAction::CompleteInitialization); + } + else + { + UpdateDescriptorSetsOnGpu(); + } return true; } diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ProgramBindings.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ProgramBindings.cpp index 78da3b996..ee3f44097 100644 --- a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ProgramBindings.cpp +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/ProgramBindings.cpp @@ -62,7 +62,7 @@ ProgramBindings::ProgramBindings(Program& program, m_has_mutable_descriptor_set = true; } - const auto destrictor_set_selector = [this, &vk_constant_descriptor_set, &vk_frame_constant_descriptor_set] + const auto descriptor_set_selector = [this, &vk_constant_descriptor_set, &vk_frame_constant_descriptor_set] (const Rhi::ProgramArgumentAccessType access_type) -> const vk::DescriptorSet& { static const vk::DescriptorSet s_empty_set; @@ -86,7 +86,7 @@ ProgramBindings::ProgramBindings(Program& program, }; // Initialize each argument binding with descriptor set pointer and binding index - ForEachArgumentBinding([&program, &destrictor_set_selector](const Rhi::IProgram::Argument& program_argument, ArgumentBinding& argument_binding) + ForEachArgumentBinding([&program, &descriptor_set_selector](const Rhi::IProgram::Argument& program_argument, ArgumentBinding& argument_binding) { const ArgumentBinding::Settings& argument_binding_settings = argument_binding.GetVulkanSettings(); const Rhi::ProgramArgumentAccessType access_type = argument_binding_settings.argument.GetAccessorType(); @@ -95,7 +95,7 @@ ProgramBindings::ProgramBindings(Program& program, META_CHECK_ARG_TRUE_DESCR(layout_argument_it != layout_info.arguments.end(), "unable to find argument '{}' in descriptor set layout", static_cast(program_argument)); const auto layout_binding_index = static_cast(std::distance(layout_info.arguments.begin(), layout_argument_it)); const uint32_t binding_value = layout_info.bindings.at(layout_binding_index).binding; - const vk::DescriptorSet& descriptor_set = destrictor_set_selector(access_type); + const vk::DescriptorSet& descriptor_set = descriptor_set_selector(access_type); argument_binding.SetDescriptorSetBinding(descriptor_set, binding_value); }); diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/RenderContext.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/RenderContext.cpp index 614bcd2cf..3a7556c86 100644 --- a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/RenderContext.cpp +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/RenderContext.cpp @@ -73,42 +73,13 @@ RenderContext::~RenderContext() } } -Ptr RenderContext::CreateCommandQueue(Rhi::CommandListType type) const +[[nodiscard]] Ptr RenderContext::CreateTexture(const Rhi::TextureSettings& settings) const { META_FUNCTION_TASK(); - return std::make_shared(*this, type); -} - -Ptr RenderContext::CreateShader(Rhi::ShaderType type, const Rhi::ShaderSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(type, *this, settings); -} + if (settings.type == Rhi::TextureType::FrameBuffer) + return std::make_shared(*this, settings, settings.frame_index_opt.value()); -Ptr RenderContext::CreateProgram(const Rhi::ProgramSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateBuffer(const Rhi::BufferSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateTexture(const Rhi::TextureSettings& settings) const -{ - META_FUNCTION_TASK(); - return settings.type == Rhi::TextureType::FrameBuffer - ? std::make_shared(*this, settings, settings.frame_index_opt.value()) - : std::make_shared(*this, settings); -} - -Ptr RenderContext::CreateSampler(const Rhi::SamplerSettings& settings) const -{ - META_FUNCTION_TASK(); - return std::make_shared(*this, settings); + return Context::CreateTexture(settings); } Ptr RenderContext::CreateRenderState(const Rhi::RenderStateSettings& settings) const diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Shader.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Shader.cpp index bc342d35b..ba8a20d7d 100644 --- a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Shader.cpp +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/Shader.cpp @@ -215,7 +215,7 @@ Ptrs Shader::GetArgumentBindings(const Rhi::Progra META_LOG("{} shader '{}' ({}) with argument bindings:", magic_enum::enum_name(GetType()), shader_settings.entry_function.function_name, - Rhi::IShader::ConvertMacroDefinitionsToString(shader_settings.compile_definitions)); + Rhi::ShaderMacroDefinition::ToString(shader_settings.compile_definitions)); Ptrs argument_bindings; const spirv_cross::Compiler& spirv_compiler = GetNativeCompiler(); @@ -332,7 +332,7 @@ void Shader::InitializeVertexInputDescriptions(const Program& program) std::stringstream log_ss; log_ss << magic_enum::enum_name(GetType()) << " shader '" << shader_settings.entry_function.function_name - << "' (" << Rhi::IShader::ConvertMacroDefinitionsToString(shader_settings.compile_definitions) + << "' (" << Rhi::ShaderMacroDefinition::ToString(shader_settings.compile_definitions) << ") input layout:" << std::endl; if (shader_resources.stage_inputs.empty()) log_ss << " - No stage inputs." << std::endl; @@ -386,9 +386,10 @@ vk::ShaderStageFlagBits Shader::ConvertTypeToStageFlagBits(Rhi::ShaderType shade META_FUNCTION_TASK(); switch(shader_type) { - case Rhi::ShaderType::All: return vk::ShaderStageFlagBits::eAll; - case Rhi::ShaderType::Vertex: return vk::ShaderStageFlagBits::eVertex; - case Rhi::ShaderType::Pixel: return vk::ShaderStageFlagBits::eFragment; + case Rhi::ShaderType::All: return vk::ShaderStageFlagBits::eAll; + case Rhi::ShaderType::Vertex: return vk::ShaderStageFlagBits::eVertex; + case Rhi::ShaderType::Pixel: return vk::ShaderStageFlagBits::eFragment; + case Rhi::ShaderType::Compute: return vk::ShaderStageFlagBits::eCompute; default: META_UNEXPECTED_ARG_RETURN(shader_type, vk::ShaderStageFlagBits::eAll); } } diff --git a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/System.cpp b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/System.cpp index eb7f989b6..fcd2640ff 100644 --- a/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/System.cpp +++ b/Modules/Graphics/RHI/Vulkan/Sources/Methane/Graphics/Vulkan/System.cpp @@ -121,11 +121,8 @@ static std::vector GetEnabledExtensions(const std::vector(context), settings, vk::ImageUsageFlagBits::eTransferDst)) + : Texture(context, settings, CreateNativeImage(dynamic_cast(context), settings, + vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc)) { } @@ -266,7 +275,7 @@ void Texture::InitializeAsImage() m_vk_unique_staging_buffer = vk_device.createBufferUnique( vk::BufferCreateInfo(vk::BufferCreateFlags{}, vk_image_memory_requirements.size, - vk::BufferUsageFlagBits::eTransferSrc, + vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst, vk::SharingMode::eExclusive) ); @@ -319,12 +328,12 @@ void Texture::ResetNativeFrameImage() ResetNativeViewDescriptors(); } -void Texture::SetData(const SubResources& sub_resources, Rhi::ICommandQueue& target_cmd_queue) +void Texture::SetData(Rhi::ICommandQueue& target_cmd_queue, const SubResources& sub_resources) { META_FUNCTION_TASK(); META_CHECK_ARG_EQUAL_DESCR(GetSettings().type, Rhi::TextureType::Image, "only image textures support data upload from CPU"); - Resource::SetData(sub_resources, target_cmd_queue); + Base::Texture::SetData(target_cmd_queue, sub_resources); m_vk_copy_regions.clear(); m_vk_copy_regions.reserve(sub_resources.size()); @@ -362,23 +371,79 @@ void Texture::SetData(const SubResources& sub_resources, Rhi::ICommandQueue& tar } // Copy buffer data from staging upload resource to the device-local GPU resource - TransferCommandList& upload_cmd_list = PrepareResourceUpload(target_cmd_queue); + TransferCommandList& upload_cmd_list = PrepareResourceTransfer(target_cmd_queue, State::CopyDest); const vk::CommandBuffer& vk_cmd_buffer = upload_cmd_list.GetNativeCommandBufferDefault(); vk_cmd_buffer.copyBufferToImage(m_vk_unique_staging_buffer.get(), GetNativeResource(), vk::ImageLayout::eTransferDstOptimal, m_vk_copy_regions); if (GetSettings().mipmapped && sub_resources.size() < GetSubresourceCount().GetRawCount()) { - CompleteResourceUpload(upload_cmd_list, GetState(), target_cmd_queue); // ownership transition only + CompleteResourceTransfer(upload_cmd_list, GetState(), target_cmd_queue); // ownership transition only GenerateMipLevels(target_cmd_queue, State::ShaderResource); } else { - CompleteResourceUpload(upload_cmd_list, State::ShaderResource, target_cmd_queue); + CompleteResourceTransfer(upload_cmd_list, State::ShaderResource, target_cmd_queue); } GetContext().RequestDeferredAction(Rhi::IContext::DeferredAction::UploadResources); } +Rhi::SubResource Texture::GetData(Rhi::ICommandQueue& target_cmd_queue, const SubResource::Index& sub_resource_index, const BytesRangeOpt& data_range) +{ + META_CHECK_ARG_EQUAL_DESCR(GetSettings().type, Rhi::TextureType::Image, "only image textures support data read-back from CPU"); + META_CHECK_ARG_TRUE_DESCR(GetUsage().HasAnyBit(Rhi::ResourceUsage::ReadBack), + "getting texture data from GPU is allowed for buffers with CPU Read-back flag only"); + + const Settings& settings = GetSettings(); + const uint32_t bytes_per_row = settings.dimensions.GetWidth() * GetPixelSize(settings.pixel_format); + const uint32_t bytes_per_image = settings.dimensions.GetHeight() * bytes_per_row; + const SubResource::Count& subresource_count = GetSubresourceCount(); + const State initial_texture_state = GetState(); + + // Copy texture data from device-local GPU resource to staging resource for read-back + vk::BufferImageCopy image_to_buffer_copy( + 0U, 0U, 0U, + vk::ImageSubresourceLayers( + Texture::GetNativeImageAspectFlags(settings), + sub_resource_index.GetMipLevel(), + sub_resource_index.GetBaseLayerIndex(subresource_count), + 1U + ), + vk::Offset3D(), + TypeConverter::FrameSizeToExtent3D(GetSettings().dimensions.AsRectSize()) + ); + TransferCommandList& upload_cmd_list = PrepareResourceTransfer(target_cmd_queue, State::CopySource); + const vk::CommandBuffer& vk_cmd_buffer = upload_cmd_list.GetNativeCommandBufferDefault(); + vk_cmd_buffer.copyImageToBuffer(GetNativeResource(), vk::ImageLayout::eTransferSrcOptimal, + m_vk_unique_staging_buffer.get(), image_to_buffer_copy); + + CompleteResourceTransfer(upload_cmd_list, initial_texture_state, target_cmd_queue); + + // Execute resource transfer commands and wait for completion + GetBaseContext().UploadResources(); + + // Map staging buffer memory and copy texture subresource data + const vk::DeviceMemory& vk_device_memory = m_vk_unique_staging_memory.get(); + Data::Size staging_data_offset = 0U; + Data::Size staging_data_size = bytes_per_image; + Data::RawPtr staging_data_ptr = nullptr; + if (data_range) + { + META_CHECK_ARG_LESS_DESCR(data_range->GetEnd(), staging_data_size, "provided texture subresource data range is out of bounds"); + staging_data_offset = data_range->GetStart(); + staging_data_size = data_range->GetLength(); + } + const vk::Result vk_map_result = GetNativeDevice().mapMemory(vk_device_memory, staging_data_offset, staging_data_size, vk::MemoryMapFlags{}, + reinterpret_cast(&staging_data_ptr)); // NOSONAR + + META_CHECK_ARG_EQUAL_DESCR(vk_map_result, vk::Result::eSuccess, "failed to map staging buffer subresource"); + META_CHECK_ARG_NOT_NULL_DESCR(staging_data_ptr, "failed to map buffer subresource"); + Rhi::SubResource sub_resource(Data::Bytes(staging_data_ptr, staging_data_ptr + staging_data_size), sub_resource_index, data_range); + + GetNativeDevice().unmapMemory(vk_device_memory); + return sub_resource; +} + bool Texture::SetName(std::string_view name) { META_FUNCTION_TASK(); @@ -511,12 +576,12 @@ void Texture::GenerateMipLevels(Rhi::ICommandQueue& target_cmd_queue, State targ SetState(target_resource_state); } -vk::ImageSubresourceRange Texture::GetNativeSubresourceRange() const noexcept +vk::ImageSubresourceRange Texture::GetNativeSubresourceRange() const { META_FUNCTION_TASK(); const SubResource::Count& subresource_count = GetSubresourceCount(); return vk::ImageSubresourceRange( - vk::ImageAspectFlagBits::eColor, + Texture::GetNativeImageAspectFlags(GetSettings()), 0U, subresource_count.GetMipLevelsCount(), 0U, subresource_count.GetBaseLayerCount() ); diff --git a/Modules/Platform/Utils/Include/Methane/Platform/Logger.h b/Modules/Platform/Utils/Include/Methane/Platform/Logger.h index 6c073ad18..018da5f88 100644 --- a/Modules/Platform/Utils/Include/Methane/Platform/Logger.h +++ b/Modules/Platform/Utils/Include/Methane/Platform/Logger.h @@ -16,8 +16,8 @@ limitations under the License. ******************************************************************************* -FILE: Methane/Data/ILogger.h -Abstract logger interface. +FILE: Methane/Platform/Logger.h +Logger implementation ******************************************************************************/ diff --git a/Modules/UserInterface/Typography/CMakeLists.txt b/Modules/UserInterface/Typography/CMakeLists.txt index bfc422745..56ad77c28 100644 --- a/Modules/UserInterface/Typography/CMakeLists.txt +++ b/Modules/UserInterface/Typography/CMakeLists.txt @@ -105,7 +105,6 @@ if(METHANE_TESTS_BUILD_ENABLED) target_link_libraries(${TEST_TARGET} PUBLIC MethaneUserInterfaceNullTypes - MethaneGraphicsRhiNullImpl MethaneDataProvider MethaneDataEvents PRIVATE diff --git a/Modules/UserInterface/Typography/Sources/Methane/UserInterface/Font.cpp b/Modules/UserInterface/Typography/Sources/Methane/UserInterface/Font.cpp index 9def4754a..1546407de 100644 --- a/Modules/UserInterface/Typography/Sources/Methane/UserInterface/Font.cpp +++ b/Modules/UserInterface/Typography/Sources/Methane/UserInterface/Font.cpp @@ -29,8 +29,7 @@ Font atlas textures generation and fonts library management classes. #include #include FT_FREETYPE_H -#include -#include +#include namespace Methane::UserInterface { @@ -55,16 +54,12 @@ FreeTypeError::FreeTypeError(FT_Error error) std::u32string Font::ConvertUtf8To32(std::string_view text) { - META_FUNCTION_TASK(); - static std::wstring_convert, char32_t> converter; - return converter.from_bytes(text.data(), text.data() + text.length()); + return nowide::utf::convert_string(text.data(), text.data() + text.size()); } std::string Font::ConvertUtf32To8(std::u32string_view text) { - META_FUNCTION_TASK(); - static std::wstring_convert, char32_t> converter; - return converter.to_bytes(text.data(), text.data() + text.length()); + return nowide::utf::convert_string(text.data(), text.data() + text.size()); } std::u32string Font::GetAlphabetInRange(char32_t from, char32_t to) diff --git a/Modules/UserInterface/Typography/Sources/Methane/UserInterface/FontImpl.hpp b/Modules/UserInterface/Typography/Sources/Methane/UserInterface/FontImpl.hpp index b74b86789..8976b6a2d 100644 --- a/Modules/UserInterface/Typography/Sources/Methane/UserInterface/FontImpl.hpp +++ b/Modules/UserInterface/Typography/Sources/Methane/UserInterface/FontImpl.hpp @@ -504,10 +504,8 @@ class Font::Impl // NOSONAR - class destructor is required, class has more than } else { - atlas_texture.SetData( - { rhi::IResource::SubResource(reinterpret_cast(m_atlas_bitmap.data()), static_cast(m_atlas_bitmap.size())) }, // NOSONAR - render_context.GetRenderCommandKit().GetQueue() - ); + atlas_texture.SetData(render_context.GetRenderCommandKit().GetQueue(), + { rhi::IResource::SubResource(reinterpret_cast(m_atlas_bitmap.data()), static_cast(m_atlas_bitmap.size())) }); // NOSONAR } return { atlas_texture, deferred_data_init }; } @@ -576,11 +574,8 @@ class Font::Impl // NOSONAR - class destructor is required, class has more than } else { - atlas_texture.texture.SetData( - rhi::IResource::SubResources - { rhi::IResource::SubResource(reinterpret_cast(m_atlas_bitmap.data()), static_cast(m_atlas_bitmap.size())) }, // NOSONAR - render_context.GetRenderCommandKit().GetQueue() - ); + atlas_texture.texture.SetData(render_context.GetRenderCommandKit().GetQueue(), + { rhi::IResource::SubResource(reinterpret_cast(m_atlas_bitmap.data()), static_cast(m_atlas_bitmap.size())) }); // NOSONAR } atlas_texture.is_update_required = false; diff --git a/Modules/UserInterface/Typography/Sources/Methane/UserInterface/Text.cpp b/Modules/UserInterface/Typography/Sources/Methane/UserInterface/Text.cpp index 0d7f0d919..5cf425ebe 100644 --- a/Modules/UserInterface/Typography/Sources/Methane/UserInterface/Text.cpp +++ b/Modules/UserInterface/Typography/Sources/Methane/UserInterface/Text.cpp @@ -48,11 +48,14 @@ Methane text rendering primitive. #include #include #include +#include namespace hlslpp // NOSONAR { #pragma pack(push, 16) + #include // NOSONAR + #pragma pack(pop) } @@ -76,7 +79,7 @@ class TextFrameResources private: uint32_t m_frame_index; - DirtyResourceMask m_dirty_mask { ~0U }; + DirtyResourceMask m_dirty_mask{ ~0U }; rhi::BufferSet m_vertex_buffer_set; rhi::Buffer m_index_buffer; rhi::Buffer m_uniforms_buffer; @@ -87,11 +90,11 @@ class TextFrameResources struct CommonResourceRefs { const rhi::RenderContext& render_context; - const rhi::RenderState& render_state; - const rhi::Buffer& const_buffer; - const rhi::Texture& atlas_texture; - const rhi::Sampler& atlas_sampler; - const TextMesh& text_mesh; + const rhi::RenderState & render_state; + const rhi::Buffer & const_buffer; + const rhi::Texture & atlas_texture; + const rhi::Sampler & atlas_sampler; + const TextMesh & text_mesh; }; TextFrameResources(uint32_t frame_index, const CommonResourceRefs& common_resources) @@ -115,11 +118,12 @@ class TextFrameResources { META_FUNCTION_TASK(); return m_dirty_mask.HasAnyBits({ - DirtyResource::Mesh, - DirtyResource::Uniforms, - DirtyResource::Atlas - }); + DirtyResource::Mesh, + DirtyResource::Uniforms, + DirtyResource::Atlas + }); } + [[nodiscard]] bool IsInitialized() const noexcept { META_FUNCTION_TASK(); @@ -139,7 +143,7 @@ class TextFrameResources return m_vertex_buffer_set; } - [[nodiscard]] const rhi::Buffer& GetIndexBuffer() const noexcept + [[nodiscard]] const rhi::Buffer& GetIndexBuffer() const noexcept { return m_index_buffer; } @@ -183,18 +187,18 @@ class TextFrameResources if (!m_vertex_buffer_set.IsInitialized() || m_vertex_buffer_set[0].GetDataSize() < vertices_data_size) { - const Data::Size vertex_buffer_size = vertices_data_size * reservation_multiplier; - rhi::Buffer vertex_buffer; + const Data::Size vertex_buffer_size = vertices_data_size * reservation_multiplier; + rhi::Buffer vertex_buffer; vertex_buffer = render_context.CreateBuffer(rhi::BufferSettings::ForVertexBuffer(vertex_buffer_size, text_mesh.GetVertexSize())); vertex_buffer.SetName(fmt::format("{} Text Vertex Buffer {}", text_name, m_frame_index)); m_vertex_buffer_set = rhi::BufferSet(rhi::BufferType::Vertex, { vertex_buffer }); } - m_vertex_buffer_set[0].SetData({ + m_vertex_buffer_set[0].SetData(render_context.GetRenderCommandKit().GetQueue(), { rhi::SubResource( reinterpret_cast(text_mesh.GetVertices().data()), vertices_data_size, // NOSONAR rhi::SubResource::Index(), rhi::BytesRange(0U, vertices_data_size) ) - }, render_context.GetRenderCommandKit().GetQueue()); + }); // Update index buffer const Data::Size indices_data_size = text_mesh.GetIndicesDataSize(); @@ -207,12 +211,12 @@ class TextFrameResources m_index_buffer.SetName(fmt::format("{} Text Index Buffer {}", text_name, m_frame_index)); } - m_index_buffer.SetData({ + m_index_buffer.SetData(render_context.GetRenderCommandKit().GetQueue(), { rhi::SubResource( reinterpret_cast(text_mesh.GetIndices().data()), indices_data_size, // NOSONAR rhi::SubResource::Index(), rhi::BytesRange(0U, indices_data_size) ) - }, render_context.GetRenderCommandKit().GetQueue()); + }); m_dirty_mask.SetBitOff(DirtyResource::Mesh); } @@ -244,19 +248,13 @@ class TextFrameResources m_program_bindings.Get({ rhi::ShaderType::Vertex, "g_uniforms" }).SetResourceViews({ { m_uniforms_buffer.GetInterface() } }); } } - m_uniforms_buffer.SetData( - rhi::SubResources - { - { reinterpret_cast(&uniforms), uniforms_data_size } // NOSONAR - }, - render_context.GetRenderCommandKit().GetQueue() - ); - + m_uniforms_buffer.SetData(render_context.GetRenderCommandKit().GetQueue(), + { reinterpret_cast(&uniforms), uniforms_data_size }); // NOSONAR m_dirty_mask.SetBitOff(DirtyResource::Uniforms); } void InitializeProgramBindings(const rhi::RenderState& state, const rhi::Buffer& const_buffer, - const rhi::Sampler& atlas_sampler, std::string_view text_name) + const rhi::Sampler& atlas_sampler, std::string_view text_name) { META_FUNCTION_TASK(); if (m_program_bindings.IsInitialized()) @@ -268,24 +266,24 @@ class TextFrameResources META_CHECK_ARG_TRUE(m_uniforms_buffer.IsInitialized()); m_program_bindings = state.GetProgram().CreateBindings({ - { { rhi::ShaderType::Vertex, "g_uniforms" }, { { m_uniforms_buffer.GetInterface() } } }, - { { rhi::ShaderType::Pixel, "g_constants" }, { { const_buffer.GetInterface() } } }, - { { rhi::ShaderType::Pixel, "g_texture" }, { { m_atlas_texture.GetInterface() } } }, - { { rhi::ShaderType::Pixel, "g_sampler" }, { { atlas_sampler.GetInterface() } } }, - }); + { { rhi::ShaderType::Vertex, "g_uniforms" }, { { m_uniforms_buffer.GetInterface() } } }, + { { rhi::ShaderType::Pixel, "g_constants" }, { { const_buffer.GetInterface() } } }, + { { rhi::ShaderType::Pixel, "g_texture" }, { { m_atlas_texture.GetInterface() } } }, + { { rhi::ShaderType::Pixel, "g_sampler" }, { { atlas_sampler.GetInterface() } } }, + }); m_program_bindings.SetName(fmt::format("{} Text Bindings {}", text_name, m_frame_index)); } }; class Text::Impl // NOSONAR - class destructor is required : public Data::Emitter - , public Data::Receiver + , public Data::Receiver { private: - using FrameResources = TextFrameResources; + using FrameResources = TextFrameResources; using PerFrameResources = std::vector; - Context& m_ui_context; + Context& m_ui_context; SettingsUtf32 m_settings; UnitRect m_frame_rect; FrameSize m_render_attachment_size = FrameSize::Max(); @@ -296,8 +294,8 @@ class Text::Impl // NOSONAR - class destructor is required rhi::Buffer m_const_buffer; rhi::Sampler m_atlas_sampler; PerFrameResources m_frame_resources; - bool m_is_viewport_dirty = true; - bool m_is_const_buffer_dirty = true; + bool m_is_viewport_dirty = true; + bool m_is_const_buffer_dirty = true; public: Impl(Context& ui_context, const rhi::RenderPattern& render_pattern, const Font& font, const SettingsUtf32& settings) @@ -316,39 +314,40 @@ class Text::Impl // NOSONAR - class destructor is required render_state_ptr) { META_CHECK_ARG_EQUAL_DESCR(render_state_ptr->GetSettings().render_pattern_ptr->GetSettings(), render_pattern.GetSettings(), - "Text '{}' render state '{}' from cache has incompatible render pattern settings", m_settings.name, m_settings.state_name); + "Text '{}' render state '{}' from cache has incompatible render pattern settings", m_settings.name, + m_settings.state_name); m_render_state = rhi::RenderState(render_state_ptr); } else { rhi::RenderState::Settings state_settings - { - rhi::Program(m_ui_context.GetRenderContext(), - rhi::Program::Settings - { - rhi::Program::ShaderSet - { - { rhi::ShaderType::Vertex, { Data::ShaderProvider::Get(), { "Text", "TextVS" }, { } } }, - { rhi::ShaderType::Pixel, { Data::ShaderProvider::Get(), { "Text", "TextPS" }, { } } }, - }, - rhi::ProgramInputBufferLayouts - { - rhi::Program::InputBufferLayout - { - rhi::Program::InputBufferLayout::ArgumentSemantics { "POSITION", "TEXCOORD" } - } - }, - rhi::ProgramArgumentAccessors - { - { { rhi::ShaderType::Vertex, "g_uniforms" }, rhi::ProgramArgumentAccessor::Type::Mutable }, - { { rhi::ShaderType::Pixel, "g_constants" }, rhi::ProgramArgumentAccessor::Type::Mutable }, - { { rhi::ShaderType::Pixel, "g_texture" }, rhi::ProgramArgumentAccessor::Type::Mutable }, - { { rhi::ShaderType::Pixel, "g_sampler" }, rhi::ProgramArgumentAccessor::Type::Constant }, - }, - render_pattern.GetAttachmentFormats() - }), - render_pattern - }; + { + rhi::Program(m_ui_context.GetRenderContext(), + rhi::Program::Settings + { + rhi::Program::ShaderSet + { + { rhi::ShaderType::Vertex, { Data::ShaderProvider::Get(), { "Text", "TextVS" }, {} } }, + { rhi::ShaderType::Pixel, { Data::ShaderProvider::Get(), { "Text", "TextPS" }, {} } }, + }, + rhi::ProgramInputBufferLayouts + { + rhi::Program::InputBufferLayout + { + rhi::Program::InputBufferLayout::ArgumentSemantics{ "POSITION", "TEXCOORD" } + } + }, + rhi::ProgramArgumentAccessors + { + { { rhi::ShaderType::Vertex, "g_uniforms" }, rhi::ProgramArgumentAccessor::Type::Mutable }, + { { rhi::ShaderType::Pixel, "g_constants" }, rhi::ProgramArgumentAccessor::Type::Mutable }, + { { rhi::ShaderType::Pixel, "g_texture" }, rhi::ProgramArgumentAccessor::Type::Mutable }, + { { rhi::ShaderType::Pixel, "g_sampler" }, rhi::ProgramArgumentAccessor::Type::Constant }, + }, + render_pattern.GetAttachmentFormats() + }), + render_pattern + }; state_settings.program.SetName("Text Shading"); state_settings.depth.enabled = false; state_settings.depth.write_enabled = false; @@ -369,12 +368,12 @@ class Text::Impl // NOSONAR - class destructor is required const FrameRect viewport_rect = m_text_mesh_ptr ? GetAlignedViewportRect() : m_frame_rect.AsBase(); m_view_state = rhi::ViewState({ - { gfx::GetFrameViewport(viewport_rect) }, - { gfx::GetFrameScissorRect(viewport_rect) } - }); + { gfx::GetFrameViewport(viewport_rect) }, + { gfx::GetFrameScissorRect(viewport_rect) } + }); - static const std::string s_sampler_name = "Font Atlas Sampler"; - if (const auto atlas_sampler_ptr = std::dynamic_pointer_cast(gfx_objects_registry.GetGraphicsObject(s_sampler_name)); + static const std::string s_sampler_name = "Font Atlas Sampler"; + if (const auto atlas_sampler_ptr = std::dynamic_pointer_cast(gfx_objects_registry.GetGraphicsObject(s_sampler_name)); atlas_sampler_ptr) { m_atlas_sampler = rhi::Sampler(atlas_sampler_ptr); @@ -382,9 +381,9 @@ class Text::Impl // NOSONAR - class destructor is required else { m_atlas_sampler = m_ui_context.GetRenderContext().CreateSampler({ - rhi::ISampler::Filter(rhi::ISampler::Filter::MinMag::Linear), - rhi::ISampler::Address(rhi::ISampler::Address::Mode::ClampToZero), - }); + rhi::ISampler::Filter(rhi::ISampler::Filter::MinMag::Linear), + rhi::ISampler::Address(rhi::ISampler::Address::Mode::ClampToZero), + }); m_atlas_sampler.SetName(s_sampler_name); gfx_objects_registry.AddGraphicsObject(m_atlas_sampler.GetInterface()); @@ -396,19 +395,19 @@ class Text::Impl // NOSONAR - class destructor is required { } Impl(Context& ui_context, const rhi::RenderPattern& render_pattern, const Font& font, const SettingsUtf8& settings) - : Impl(ui_context, render_pattern, font, - SettingsUtf32 - { - settings.name, - Font::ConvertUtf8To32(settings.text), - settings.rect, - settings.layout, - settings.color, - settings.incremental_update, - settings.adjust_vertical_content_offset, - settings.mesh_buffers_reservation_multiplier, - settings.state_name - } + : Impl(ui_context, render_pattern, font, + SettingsUtf32 + { + settings.name, + Font::ConvertUtf8To32(settings.text), + settings.rect, + settings.layout, + settings.color, + settings.incremental_update, + settings.adjust_vertical_content_offset, + settings.mesh_buffers_reservation_multiplier, + settings.state_name + } ) { } @@ -425,10 +424,16 @@ class Text::Impl // NOSONAR - class destructor is required m_font.Disconnect(*this); } - [[nodiscard]] const UnitRect& GetFrameRect() const noexcept { return m_frame_rect; } - [[nodiscard]] const SettingsUtf32& GetSettings() const noexcept { return m_settings; } - [[nodiscard]] const std::u32string& GetTextUtf32() const noexcept { return m_settings.text; } - [[nodiscard]] std::string GetTextUtf8() const + [[nodiscard]] const UnitRect& GetFrameRect() const noexcept + { return m_frame_rect; } + + [[nodiscard]] const SettingsUtf32& GetSettings() const noexcept + { return m_settings; } + + [[nodiscard]] const std::u32string& GetTextUtf32() const noexcept + { return m_settings.text; } + + [[nodiscard]] std::string GetTextUtf8() const { META_FUNCTION_TASK(); return Font::ConvertUtf32To8(m_settings.text); @@ -567,7 +572,8 @@ class Text::Impl // NOSONAR - class destructor is required } if (frame_resources.IsDirty(FrameResources::DirtyResource::Mesh) && m_text_mesh_ptr) { - frame_resources.UpdateMeshBuffers(m_ui_context.GetRenderContext(), *m_text_mesh_ptr, m_settings.name, m_settings.mesh_buffers_reservation_multiplier); + frame_resources.UpdateMeshBuffers(m_ui_context.GetRenderContext(), *m_text_mesh_ptr, m_settings.name, + m_settings.mesh_buffers_reservation_multiplier); } if (frame_resources.IsDirty(FrameResources::DirtyResource::Atlas)) { @@ -608,7 +614,7 @@ class Text::Impl // NOSONAR - class destructor is required META_FUNCTION_TASK(); META_UNUSED(old_atlas_texture_ptr); if (m_font != font || m_frame_resources.empty() || - (new_atlas_texture_ptr && m_ui_context.GetRenderContext() != new_atlas_texture_ptr->GetRenderContext())) + (new_atlas_texture_ptr && m_ui_context.GetRenderContext().GetInterfacePtr().get() != std::addressof(new_atlas_texture_ptr->GetContext()))) return; MakeFrameResourcesDirty(FrameResources::DirtyResourceMask(FrameResources::DirtyResource::Atlas)); @@ -738,14 +744,8 @@ class Text::Impl // NOSONAR - class destructor is required const hlslpp::TextConstants constants{ m_settings.color.AsVector() }; - m_const_buffer.SetData( - rhi::SubResources - { - rhi::IResource::SubResource(reinterpret_cast(&constants), // NOSONAR - static_cast(sizeof(constants))) - }, - m_ui_context.GetRenderContext().GetRenderCommandKit().GetQueue() - ); + m_const_buffer.SetData(m_ui_context.GetRenderContext().GetRenderCommandKit().GetQueue(), + { reinterpret_cast(&constants), static_cast(sizeof(constants)) }); // NOSONAR m_is_const_buffer_dirty = false; } diff --git a/Modules/UserInterface/Typography/Sources/Methane/UserInterface/TextMesh.cpp b/Modules/UserInterface/Typography/Sources/Methane/UserInterface/TextMesh.cpp index 99ef6823b..e4670de64 100644 --- a/Modules/UserInterface/Typography/Sources/Methane/UserInterface/TextMesh.cpp +++ b/Modules/UserInterface/Typography/Sources/Methane/UserInterface/TextMesh.cpp @@ -370,10 +370,13 @@ void TextMesh::ApplyAlignmentOffset(const size_t aligned_text_length, const size for(size_t char_index = line_start_index; char_index < end_char_index; ++char_index) { const CharPosition& char_position = m_char_positions[char_index]; - if (char_position.is_line_start && char_index <= end_char_index - 1) + if (char_position.is_line_start && + !char_position.is_whitespace && + char_index <= end_char_index - 1) { + META_CHECK_ARG_LESS_DESCR(char_position.start_vertex_index, m_vertices.size(), "character start vertex index is invalid"); line_whitespace_index = 0; - line_start_offset = static_cast(m_vertices[m_char_positions[char_index].start_vertex_index].position[0]); + line_start_offset = static_cast(m_vertices[char_position.start_vertex_index].position[0]); horizontal_alignment_offset = GetHorizontalLineAlignmentOffset(char_index); if (justify_alignment_enabled) { diff --git a/Modules/UserInterface/Widgets/Sources/Methane/UserInterface/HeadsUpDisplay.cpp b/Modules/UserInterface/Widgets/Sources/Methane/UserInterface/HeadsUpDisplay.cpp index 124d2b728..cf6af9d70 100644 --- a/Modules/UserInterface/Widgets/Sources/Methane/UserInterface/HeadsUpDisplay.cpp +++ b/Modules/UserInterface/Widgets/Sources/Methane/UserInterface/HeadsUpDisplay.cpp @@ -37,7 +37,7 @@ Heads-Up-Display widget for displaying runtime rendering parameters. #include #include -#include +#include #include #include #include @@ -275,7 +275,7 @@ void HeadsUpDisplay::Update(const FrameSize& render_attachment_size) return; } - const rhi::IFpsCounter& fps_counter = GetUIContext().GetRenderContext().GetFpsCounter(); + const Data::IFpsCounter& fps_counter = GetUIContext().GetRenderContext().GetFpsCounter(); const rhi::RenderContextSettings& context_settings = GetUIContext().GetRenderContext().GetSettings(); GetTextBlock(TextBlock::Fps).SetText(fmt::format("{:d} FPS", fps_counter.GetFramesPerSecond())); diff --git a/README.md b/README.md index 49979b34a..35e5fff4e 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ and continue with others. | 5. [Typography](/Apps/05-Typography) | ![Typography on Windows](/Apps/05-Typography/Screenshots/TypographyWinDirectX12.jpg) | Typography demonstrates animated text rendering with dynamic font atlas updates using Methane UI. | | 6. [Cube-Map Array](/Apps/06-CubeMapArray) | ![Cube-Map Array on Windows](/Apps/06-CubeMapArray/Screenshots/CubeMapArrayWinDirectX12.jpg) | Cube-map array texturing along with sky-box rendering. | | 7. [Parallel Rendering](/Apps/07-ParallelRendering) | ![Parallel Rendering on Windows](/Apps/07-ParallelRendering/Screenshots/ParallelRenderingWinDirectX12.jpg) | Parallel rendering of the textured cube instances to the single render pass. | +| 8. [Console Compute](/Apps/08-ConsoleCompute) | ![Console Compute on Windows](/Apps/08-ConsoleCompute/Screenshots/ConsoleComputeWinDirectX12.jpg) | Conway's Game of Life implemented in Compute Shader and running in pure console application. | ### Samples diff --git a/Tests/Coverage/GCov/CMakeLists.txt b/Tests/Coverage/GCov/CMakeLists.txt index fe8468291..1e45d4fb6 100644 --- a/Tests/Coverage/GCov/CMakeLists.txt +++ b/Tests/Coverage/GCov/CMakeLists.txt @@ -7,6 +7,7 @@ list(APPEND TEST_TARGETS MethanePlatformInputTest MethaneGraphicsCameraTest MethaneGraphicsTypesTest + MethaneGraphicsRhiTest MethaneUserInterfaceTypesTest ) @@ -25,7 +26,7 @@ endif() setup_target_for_coverage_lcov( NAME MethaneTestCoverage - EXECUTABLE ctest -j 4 + EXECUTABLE ctest -j 4 || true # Suppress error on test failures DEPENDENCIES ${TEST_TARGETS} EXCLUDE ${EXCLUDE_DIRS} ) \ No newline at end of file diff --git a/Tests/Graphics/CMakeLists.txt b/Tests/Graphics/CMakeLists.txt index 688149130..c0c27699d 100644 --- a/Tests/Graphics/CMakeLists.txt +++ b/Tests/Graphics/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(Types) add_subdirectory(Camera) +add_subdirectory(RHI) diff --git a/Tests/Graphics/RHI/BufferTest.cpp b/Tests/Graphics/RHI/BufferTest.cpp new file mode 100644 index 000000000..340cc481e --- /dev/null +++ b/Tests/Graphics/RHI/BufferTest.cpp @@ -0,0 +1,171 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/BufferTest.cpp +Unit-tests of the RHI Buffer + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Buffer Functions", "[rhi][buffer][resource]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + const Rhi::BufferSettings constant_buffer_settings = Rhi::BufferSettings::ForConstantBuffer(42000, false, true); + + SECTION("Constant Buffer Construction") + { + Rhi::Buffer buffer; + REQUIRE_NOTHROW(buffer = compute_context.CreateBuffer(constant_buffer_settings)); + REQUIRE(buffer.IsInitialized()); + CHECK(buffer.GetInterfacePtr()); + CHECK(buffer.GetResourceType() == Rhi::ResourceType::Buffer); + CHECK(buffer.GetSettings() == constant_buffer_settings); + CHECK(buffer.GetUsage() == constant_buffer_settings.usage_mask); + CHECK(std::addressof(buffer.GetContext()) == compute_context.GetInterfacePtr().get()); + } + + SECTION("Object Destroyed Callback") + { + auto buffer_ptr = std::make_unique(compute_context, constant_buffer_settings); + ObjectCallbackTester object_callback_tester(*buffer_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + buffer_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + SECTION("Resource Released Callback") + { + auto buffer_ptr = std::make_unique(compute_context, constant_buffer_settings); + ResourceCallbackTester resource_callback_tester(*buffer_ptr); + CHECK_FALSE(resource_callback_tester.IsResourceReleased()); + buffer_ptr.reset(); + CHECK(resource_callback_tester.IsResourceReleased()); + } + + const Rhi::Buffer buffer = compute_context.CreateBuffer(constant_buffer_settings); + + SECTION("Object Name Setup") + { + CHECK(buffer.SetName("My Buffer")); + CHECK(buffer.GetName() == "My Buffer"); + } + + SECTION("Object Name Change Callback") + { + CHECK(buffer.SetName("My Buffer")); + ObjectCallbackTester object_callback_tester(buffer); + CHECK(buffer.SetName("Our Buffer")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Buffer"); + CHECK(object_callback_tester.GetOldObjectName() == "My Buffer"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(buffer.SetName("My Buffer")); + ObjectCallbackTester object_callback_tester(buffer); + CHECK_FALSE(buffer.SetName("My Buffer")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Set State") + { + CHECK(buffer.GetState() == Rhi::ResourceState::Undefined); + CHECK(buffer.SetState(Rhi::ResourceState::ShaderResource)); + CHECK(buffer.GetState() == Rhi::ResourceState::ShaderResource); + } + + SECTION("Set State with Barriers") + { + Rhi::ResourceBarriers resource_barriers; + CHECK(buffer.SetState(Rhi::ResourceState::CopyDest)); + CHECK(buffer.SetState(Rhi::ResourceState::ShaderResource, resource_barriers)); + CHECK(buffer.GetState() == Rhi::ResourceState::ShaderResource); + CHECK(resource_barriers.HasStateTransition(buffer.GetInterface(), + Rhi::ResourceState::CopyDest, + Rhi::ResourceState::ShaderResource)); + } + + SECTION("Set Owner Queue Family") + { + CHECK_FALSE(buffer.GetOwnerQueueFamily().has_value()); + CHECK(buffer.SetOwnerQueueFamily(1U)); + REQUIRE(buffer.GetOwnerQueueFamily().has_value()); + CHECK(buffer.GetOwnerQueueFamily().value() == 1U); + } + + SECTION("Set Owner Queue Family with Barriers") + { + Rhi::ResourceBarriers resource_barriers; + CHECK(buffer.SetOwnerQueueFamily(0U)); + CHECK(buffer.SetOwnerQueueFamily(1U, resource_barriers)); + REQUIRE(buffer.GetOwnerQueueFamily().has_value()); + CHECK(buffer.GetOwnerQueueFamily().value() == 1U); + CHECK(resource_barriers.HasOwnerTransition(buffer.GetInterface(), 0U, 1U)); + } + + SECTION("Restore Descriptor Views") + { + auto buffer_ptr = std::make_unique(compute_context, constant_buffer_settings); + const Rhi::Buffer::DescriptorByViewId descriptor_by_view_id = buffer_ptr->GetDescriptorByViewId(); + buffer_ptr = std::make_unique(compute_context, constant_buffer_settings); + CHECK_NOTHROW(buffer_ptr->RestoreDescriptorViews(descriptor_by_view_id)); + } + + SECTION("Get Data Size") + { + CHECK(buffer.GetDataSize(Data::MemoryState::Reserved) == Rhi::BufferSettings::GetAlignedSize(constant_buffer_settings.size)); + CHECK(buffer.GetDataSize(Data::MemoryState::Initialized) == 0U); + } + + SECTION("Set Data and Get Formatted Items Count") + { + const Rhi::BufferSettings vertex_buffer_settings = Rhi::BufferSettings::ForVertexBuffer(24 * 512, 24, true); + const Rhi::Buffer vertex_buffer = compute_context.CreateBuffer(vertex_buffer_settings); + CHECK(vertex_buffer.GetFormattedItemsCount() == 0U); + + std::vector test_data(24 * 256, std::byte(8)); + REQUIRE_NOTHROW(vertex_buffer.SetData(compute_context.GetUploadCommandKit().GetQueue(), { + reinterpret_cast(test_data.data()), // NOSONAR + static_cast(test_data.size()) + })); + CHECK(vertex_buffer.GetFormattedItemsCount() == 256); + } + + SECTION("Get Data") + { + CHECK_NOTHROW(buffer.GetData(compute_context.GetUploadCommandKit().GetQueue())); + } +} diff --git a/Tests/Graphics/RHI/CMakeLists.txt b/Tests/Graphics/RHI/CMakeLists.txt new file mode 100644 index 000000000..d65a0d8f8 --- /dev/null +++ b/Tests/Graphics/RHI/CMakeLists.txt @@ -0,0 +1,45 @@ +set(TARGET MethaneGraphicsRhiTest) + +add_executable(${TARGET} + RhiTestHelpers.hpp + ShaderTest.cpp + ProgramTest.cpp + ProgramBindingsTest.cpp + ComputeContextTest.cpp + ComputeStateTest.cpp + CommandQueueTest.cpp + FenceTest.cpp + TransferCommandListTest.cpp + ComputeCommandListTest.cpp + BufferTest.cpp + SamplerTest.cpp + TextureTest.cpp +) + +target_link_libraries(${TARGET} + PRIVATE + MethaneBuildOptions + MethaneGraphicsRhiNullImpl + MethaneGraphicsRhiNull + TaskFlow + magic_enum + $<$:TracyClient> + Catch2WithMain +) + +if(METHANE_PRECOMPILED_HEADERS_ENABLED) + target_precompile_headers(${TARGET} REUSE_FROM MethaneGraphicsRhiNullImpl) +endif() + +set_target_properties(${TARGET} + PROPERTIES + FOLDER Tests +) + +install(TARGETS ${TARGET} + RUNTIME + DESTINATION Tests + COMPONENT Test +) + +include(CatchDiscoverAndRunTests) diff --git a/Tests/Graphics/RHI/CommandQueueTest.cpp b/Tests/Graphics/RHI/CommandQueueTest.cpp new file mode 100644 index 000000000..97aeff6dc --- /dev/null +++ b/Tests/Graphics/RHI/CommandQueueTest.cpp @@ -0,0 +1,155 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/CommandQueueTest.cpp +Unit-tests of the RHI Command Queue + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Command Queue Functions", "[rhi][queue]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + + SECTION("Command Queue Construction") + { + Rhi::CommandQueue cmd_queue; + REQUIRE_NOTHROW(cmd_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Compute)); + REQUIRE(cmd_queue.IsInitialized()); + CHECK(cmd_queue.GetInterfacePtr()); + CHECK(cmd_queue.GetCommandListType() == Rhi::CommandListType::Compute); + } + + SECTION("Object Destroyed Callback") + { + auto cmd_queue_ptr = std::make_unique(compute_context, Rhi::CommandListType::Compute); + ObjectCallbackTester object_callback_tester(*cmd_queue_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + cmd_queue_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + const Rhi::CommandQueue cmd_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Compute); + + SECTION("Object Name Setup") + { + CHECK(cmd_queue.SetName("My Compute Command Queue")); + CHECK(cmd_queue.GetName() == "My Compute Command Queue"); + } + + SECTION("Object Name Change Callback") + { + CHECK(cmd_queue.SetName("My Compute Command Queue")); + ObjectCallbackTester object_callback_tester(cmd_queue); + CHECK(cmd_queue.SetName("Our Compute Command Queue")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Compute Command Queue"); + CHECK(object_callback_tester.GetOldObjectName() == "My Compute Command Queue"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(cmd_queue.SetName("My Compute Command Queue")); + ObjectCallbackTester object_callback_tester(cmd_queue); + CHECK_FALSE(cmd_queue.SetName("My Compute Command Queue")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Execute Command Lists") + { + const Rhi::CommandQueue compute_cmd_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Compute); + const Rhi::ComputeCommandList compute_cmd_list = compute_cmd_queue.CreateComputeCommandList(); + const Rhi::CommandListSet cmd_list_set({ compute_cmd_list.GetInterface() }); + + REQUIRE_NOTHROW(compute_cmd_list.Reset()); + REQUIRE_NOTHROW(compute_cmd_list.Commit()); + CHECK(compute_cmd_list.GetState() == Rhi::CommandListState::Committed); + + Rhi::ICommandList* completed_command_list_ptr = nullptr; + REQUIRE_NOTHROW(compute_cmd_queue.Execute(cmd_list_set, + [&completed_command_list_ptr](Rhi::ICommandList& command_list) { + completed_command_list_ptr = &command_list; + })); + + CHECK(compute_cmd_list.GetState() == Rhi::CommandListState::Executing); + CHECK_FALSE(completed_command_list_ptr); + + dynamic_cast(cmd_list_set.GetInterface()).Complete(); + + CHECK(compute_cmd_list.GetState() == Rhi::CommandListState::Pending); + CHECK(completed_command_list_ptr == compute_cmd_list.GetInterfacePtr().get()); + } +} + +TEST_CASE("RHI Compute Command Queue Factory", "[rhi][compute][context][factory]") +{ + const Rhi::ComputeContext compute_context(GetTestDevice(), g_parallel_executor, {}); + const Rhi::CommandQueue compute_cmd_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Compute); + + SECTION("Can Create Command Kit") + { + Rhi::CommandKit command_kit; + REQUIRE_NOTHROW(command_kit = compute_cmd_queue.CreateCommandKit()); + REQUIRE(command_kit.IsInitialized()); + CHECK(command_kit.GetListType() == Rhi::CommandListType::Compute); + CHECK(command_kit.GetQueue().GetInterfacePtr().get() == compute_cmd_queue.GetInterfacePtr().get()); + } + + SECTION("Can Create Fence") + { + Rhi::Fence fence; + REQUIRE_NOTHROW(fence = compute_cmd_queue.CreateFence()); + REQUIRE(fence.IsInitialized()); + } + + SECTION("Can Create Transfer Command List") + { + Rhi::TransferCommandList transfer_cmd_list; + REQUIRE_NOTHROW(transfer_cmd_list = compute_cmd_queue.CreateTransferCommandList()); + REQUIRE(transfer_cmd_list.IsInitialized()); + CHECK(transfer_cmd_list.GetCommandQueue().GetInterfacePtr().get() == compute_cmd_queue.GetInterfacePtr().get()); + } + + SECTION("Can Create Compute Command List") + { + Rhi::ComputeCommandList compute_cmd_list; + REQUIRE_NOTHROW(compute_cmd_list = compute_cmd_queue.CreateComputeCommandList()); + REQUIRE(compute_cmd_list.IsInitialized()); + CHECK(compute_cmd_list.GetCommandQueue().GetInterfacePtr().get() == compute_cmd_queue.GetInterfacePtr().get()); + } +} diff --git a/Tests/Graphics/RHI/ComputeCommandListTest.cpp b/Tests/Graphics/RHI/ComputeCommandListTest.cpp new file mode 100644 index 000000000..18ad47273 --- /dev/null +++ b/Tests/Graphics/RHI/ComputeCommandListTest.cpp @@ -0,0 +1,360 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/ComputeCommandListTest.cpp +Unit-tests of the RHI Compute Command List + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Compute Command List Functions", "[rhi][list][compute]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + const Rhi::CommandQueue compute_cmd_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Compute); + + const Rhi::Program compute_program = [&compute_context]() + { + const Rhi::ProgramArgumentAccessor texture_accessor{ Rhi::ShaderType::Compute, "InTexture", Rhi::ProgramArgumentAccessType::Constant }; + const Rhi::ProgramArgumentAccessor sampler_accessor{ Rhi::ShaderType::Compute, "InSampler", Rhi::ProgramArgumentAccessType::Constant }; + const Rhi::ProgramArgumentAccessor buffer_accessor { Rhi::ShaderType::Compute, "OutBuffer", Rhi::ProgramArgumentAccessType::Mutable }; + Rhi::Program compute_program = compute_context.CreateProgram( + Rhi::ProgramSettingsImpl + { + Rhi::ProgramSettingsImpl::ShaderSet + { + { Rhi::ShaderType::Compute, { Data::ShaderProvider::Get(), { "Compute", "Main" } } } + }, + Rhi::ProgramInputBufferLayouts{ }, + Rhi::ProgramArgumentAccessors + { + texture_accessor, + sampler_accessor, + buffer_accessor + } + }); + dynamic_cast(compute_program.GetInterface()).SetArgumentBindings({ + { texture_accessor, { Rhi::ResourceType::Texture, 1U } }, + { sampler_accessor, { Rhi::ResourceType::Sampler, 1U } }, + { buffer_accessor, { Rhi::ResourceType::Buffer, 1U } }, + }); + return compute_program; + }(); + + const Rhi::ComputeState compute_state = compute_context.CreateComputeState({ + compute_program, + Rhi::ThreadGroupSize(16, 16, 1) + }); + + SECTION("Transfer Command List Construction") + { + Rhi::ComputeCommandList cmd_list; + REQUIRE_NOTHROW(cmd_list = compute_cmd_queue.CreateComputeCommandList()); + REQUIRE(cmd_list.IsInitialized()); + CHECK(cmd_list.GetInterfacePtr()); + CHECK(cmd_list.GetCommandQueue().GetInterfacePtr().get() == compute_cmd_queue.GetInterfacePtr().get()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Pending); + } + + SECTION("Object Destroyed Callback") + { + auto cmd_list_ptr = std::make_unique(compute_cmd_queue); + ObjectCallbackTester object_callback_tester(*cmd_list_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + cmd_list_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + const Rhi::ComputeCommandList cmd_list = compute_cmd_queue.CreateComputeCommandList(); + + SECTION("Object Name Setup") + { + CHECK(cmd_list.SetName("My Command List")); + CHECK(cmd_list.GetName() == "My Command List"); + } + + SECTION("Object Name Change Callback") + { + CHECK(cmd_list.SetName("My Command List")); + ObjectCallbackTester object_callback_tester(cmd_list); + CHECK(cmd_list.SetName("Our Command List")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Command List"); + CHECK(object_callback_tester.GetOldObjectName() == "My Command List"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(cmd_list.SetName("My Fence")); + ObjectCallbackTester object_callback_tester(cmd_list); + CHECK_FALSE(cmd_list.SetName("My Fence")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Reset Command List") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + } + + SECTION("Reset Command List Once") + { + REQUIRE_NOTHROW(cmd_list.ResetOnce()); + REQUIRE_NOTHROW(cmd_list.ResetOnce()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + } + + SECTION("Reset Command List with Debug Group") + { + const Rhi::CommandListDebugGroup debug_group("Test"); + REQUIRE_NOTHROW(cmd_list.Reset(&debug_group)); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK(dynamic_cast(cmd_list.GetInterface()).GetTopOpenDebugGroup()->GetName() == "Test"); + } + + SECTION("Reset Command List Once with Debug Group") + { + const Rhi::CommandListDebugGroup debug_group("Test"); + REQUIRE_NOTHROW(cmd_list.ResetOnce(&debug_group)); + REQUIRE_NOTHROW(cmd_list.ResetOnce(&debug_group)); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK(dynamic_cast(cmd_list.GetInterface()).GetTopOpenDebugGroup()->GetName() == "Test"); + } + + SECTION("Push and Pop Debug Group") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK_NOTHROW(cmd_list.PushDebugGroup(Rhi::CommandListDebugGroup("Test"))); + CHECK_NOTHROW(cmd_list.PopDebugGroup()); + } + + SECTION("Can not Pop Missing Debug Group") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK_THROWS(cmd_list.PopDebugGroup()); + } + + SECTION("Set Program Bindings") + { + const Rhi::Texture texture = [&compute_context]() + { + Rhi::Texture texture = compute_context.CreateTexture(Rhi::TextureSettings::ForImage(Dimensions(640, 480), {}, PixelFormat::RGBA8, false)); + texture.SetName("T"); + return texture; + }(); + + const Rhi::Sampler sampler = [&compute_context]() + { + const Rhi::Sampler sampler = compute_context.CreateSampler({ + rhi::SamplerFilter { rhi::SamplerFilter::MinMag::Linear }, + rhi::SamplerAddress { rhi::SamplerAddress::Mode::ClampToEdge } + }); + sampler.SetName("S"); + return sampler; + }(); + + const Rhi::Buffer buffer = [&compute_context]() + { + const Rhi::Buffer buffer = compute_context.CreateBuffer(Rhi::BufferSettings::ForConstantBuffer(42000, false, true)); + buffer.SetName("B"); + return buffer; + }(); + + const Rhi::ProgramBindings compute_program_bindings = compute_program.CreateBindings({ + { { Rhi::ShaderType::Compute, "InTexture" }, { { texture.GetInterface() } } }, + { { Rhi::ShaderType::Compute, "InSampler" }, { { sampler.GetInterface() } } }, + { { Rhi::ShaderType::Compute, "OutBuffer" }, { { buffer.GetInterface() } } }, + }); + + REQUIRE_NOTHROW(cmd_list.ResetWithState(compute_state)); + REQUIRE_NOTHROW(cmd_list.SetProgramBindings(compute_program_bindings)); + REQUIRE_NOTHROW(cmd_list.Commit()); + CHECK(dynamic_cast(cmd_list.GetInterface()).GetProgramBindingsPtr() == compute_program_bindings.GetInterfacePtr().get()); + } + + SECTION("Set Resource Barriers") + { + const Rhi::ResourceBarriers barriers(Rhi::IResourceBarriers::Set{}); + REQUIRE_NOTHROW(cmd_list.Reset()); + REQUIRE_NOTHROW(cmd_list.SetResourceBarriers(barriers.GetInterface())); + } + + SECTION("Commit Command List") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK_NOTHROW(cmd_list.Commit()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Committed); + } + + SECTION("Execute Command List with Callback Tracker") + { + CommandListCallbackTester cmd_list_callback_tester(cmd_list); + const Rhi::CommandListSet cmd_list_set({ cmd_list.GetInterface() }); + + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK(cmd_list_callback_tester.GetTrackingState() == Rhi::CommandListState::Encoding); + CHECK(cmd_list_callback_tester.IsStateChanged()); + CHECK_FALSE(cmd_list_callback_tester.IsExecutionCompleted()); + + cmd_list_callback_tester.Reset(); + + REQUIRE_NOTHROW(cmd_list.Commit()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Committed); + CHECK(cmd_list_callback_tester.GetTrackingState() == Rhi::CommandListState::Committed); + CHECK(cmd_list_callback_tester.IsStateChanged()); + CHECK_FALSE(cmd_list_callback_tester.IsExecutionCompleted()); + + cmd_list_callback_tester.Reset(); + + Rhi::ICommandList* completed_command_list_ptr = nullptr; + REQUIRE_NOTHROW(compute_cmd_queue.Execute(cmd_list_set, + [&completed_command_list_ptr](Rhi::ICommandList& command_list) { + completed_command_list_ptr = &command_list; + })); + + CHECK(cmd_list.GetState() == Rhi::CommandListState::Executing); + CHECK(cmd_list_callback_tester.GetTrackingState() == Rhi::CommandListState::Executing); + CHECK(cmd_list_callback_tester.IsStateChanged()); + CHECK_FALSE(cmd_list_callback_tester.IsExecutionCompleted()); + CHECK_FALSE(completed_command_list_ptr); + + cmd_list_callback_tester.Reset(); + dynamic_cast(cmd_list_set.GetInterface()).Complete(); + + CHECK(cmd_list.GetState() == Rhi::CommandListState::Pending); + CHECK(cmd_list_callback_tester.GetTrackingState() == Rhi::CommandListState::Pending); + CHECK(cmd_list_callback_tester.IsExecutionCompleted()); + CHECK(completed_command_list_ptr == cmd_list.GetInterfacePtr().get()); + } + + SECTION("Wait Until Command List Completed") + { + const Rhi::CommandListSet cmd_list_set({ cmd_list.GetInterface() }); + REQUIRE_NOTHROW(cmd_list.Reset()); + REQUIRE_NOTHROW(cmd_list.Commit()); + REQUIRE_NOTHROW(compute_cmd_queue.Execute(cmd_list_set)); + + auto async_complete = std::async(std::launch::async, [&cmd_list_set]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + dynamic_cast(cmd_list_set.GetInterface()).Complete(); + }); + + CHECK(cmd_list.GetState() == Rhi::CommandListState::Executing); + REQUIRE_NOTHROW(cmd_list.WaitUntilCompleted()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Pending); + } + + SECTION("Get GPU Time Range") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK_NOTHROW(cmd_list.GetGpuTimeRange(true) == Data::TimeRange{}); + CHECK_NOTHROW(cmd_list.GetGpuTimeRange(false) == Data::TimeRange{}); + } + + SECTION("Reset Command List with Compute State") + { + REQUIRE_NOTHROW(cmd_list.ResetWithState(compute_state)); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK(&dynamic_cast(cmd_list.GetInterface()).GetComputeState() == compute_state.GetInterfacePtr().get()); + } + + SECTION("Reset Command List Once with Compute State") + { + REQUIRE_NOTHROW(cmd_list.ResetWithStateOnce(compute_state)); + REQUIRE_NOTHROW(cmd_list.ResetWithStateOnce(compute_state)); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK(&dynamic_cast(cmd_list.GetInterface()).GetComputeState() == compute_state.GetInterfacePtr().get()); + } + + SECTION("Reset Command List with Compute State and Debug Group") + { + const Rhi::CommandListDebugGroup debug_group("Test"); + REQUIRE_NOTHROW(cmd_list.ResetWithState(compute_state, &debug_group)); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + + auto& null_cmd_list = dynamic_cast(cmd_list.GetInterface()); + CHECK(null_cmd_list.GetTopOpenDebugGroup()->GetName() == "Test"); + CHECK(&null_cmd_list.GetComputeState() == compute_state.GetInterfacePtr().get()); + } + + SECTION("Reset Command List Once with Compute State and Debug Group") + { + const Rhi::CommandListDebugGroup debug_group("Test"); + REQUIRE_NOTHROW(cmd_list.ResetWithStateOnce(compute_state, &debug_group)); + REQUIRE_NOTHROW(cmd_list.ResetWithStateOnce(compute_state, &debug_group)); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + + auto& null_cmd_list = dynamic_cast(cmd_list.GetInterface()); + CHECK(null_cmd_list.GetTopOpenDebugGroup()->GetName() == "Test"); + CHECK(&null_cmd_list.GetComputeState() == compute_state.GetInterfacePtr().get()); + } + + SECTION("Set Command List Compute State") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + REQUIRE_NOTHROW(cmd_list.SetComputeState(compute_state)); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + + auto& null_cmd_list = dynamic_cast(cmd_list.GetInterface()); + CHECK(&null_cmd_list.GetComputeState() == compute_state.GetInterfacePtr().get()); + } + + SECTION("Dispatch thread groups in Compute Command List") + { + const Rhi::CommandListSet cmd_list_set({ cmd_list.GetInterface() }); + REQUIRE_NOTHROW(cmd_list.ResetWithState(compute_state)); + REQUIRE_NOTHROW(cmd_list.Dispatch(Rhi::ThreadGroupsCount(4U, 4U, 1U))); + REQUIRE_NOTHROW(cmd_list.Commit()); + REQUIRE_NOTHROW(compute_cmd_queue.Execute(cmd_list_set)); + dynamic_cast(cmd_list_set.GetInterface()).Complete(); + } +} diff --git a/Tests/Graphics/RHI/ComputeContextTest.cpp b/Tests/Graphics/RHI/ComputeContextTest.cpp new file mode 100644 index 000000000..d202643bb --- /dev/null +++ b/Tests/Graphics/RHI/ComputeContextTest.cpp @@ -0,0 +1,333 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/ComputeContextTest.cpp +Unit-tests of the RHI ComputeContext + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Compute Context Functions", "[rhi][compute][context]") +{ + const Rhi::ComputeContextSettings compute_context_settings{ + Rhi::ContextOptionMask{ Rhi::ContextOption::TransferWithD3D12DirectQueue } + }; + + SECTION("Context Construction") + { + Rhi::ComputeContext compute_context; + REQUIRE_NOTHROW(compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, compute_context_settings)); + REQUIRE(compute_context.IsInitialized()); + CHECK(compute_context.GetInterfacePtr()); + CHECK(compute_context.GetSettings() == compute_context_settings); + CHECK(compute_context.GetOptions() == compute_context_settings.options); + CHECK(compute_context.GetName() == ""); + CHECK(compute_context.GetDevice() == GetTestDevice()); + CHECK(std::addressof(compute_context.GetParallelExecutor()) == std::addressof(g_parallel_executor)); + } + + SECTION("Object Destroyed Callback") + { + auto compute_context_ptr = std::make_unique(GetTestDevice(), g_parallel_executor, compute_context_settings); + ObjectCallbackTester object_callback_tester(*compute_context_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + compute_context_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + const Rhi::ComputeContext compute_context(GetTestDevice(), g_parallel_executor, compute_context_settings); + + SECTION("Object Name Setup") + { + CHECK(compute_context.SetName("My Compute Context")); + CHECK(compute_context.GetName() == "My Compute Context"); + } + + SECTION("Object Name Change Callback") + { + CHECK(compute_context.SetName("My Compute Context")); + ObjectCallbackTester object_callback_tester(compute_context); + CHECK(compute_context.SetName("Our Compute Context")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Compute Context"); + CHECK(object_callback_tester.GetOldObjectName() == "My Compute Context"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(compute_context.SetName("My Compute Context")); + ObjectCallbackTester object_callback_tester(compute_context); + CHECK_FALSE(compute_context.SetName("My Compute Context")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Context Reset") + { + ContextCallbackTester context_callback_tester(compute_context); + REQUIRE_NOTHROW(compute_context.Reset()); + CHECK(context_callback_tester.IsContextReleased()); + CHECK_FALSE(context_callback_tester.IsContextCompletingInitialization()); + CHECK(context_callback_tester.IsContextInitialized()); + } + + SECTION("Context Reset with Device") + { + ContextCallbackTester context_callback_tester(compute_context); + const Rhi::Device new_device = Rhi::System::Get().UpdateGpuDevices().at(0); + REQUIRE_NOTHROW(compute_context.Reset(new_device)); + CHECK(context_callback_tester.IsContextReleased()); + CHECK_FALSE(context_callback_tester.IsContextCompletingInitialization()); + CHECK(context_callback_tester.IsContextInitialized()); + CHECK(compute_context.GetDevice() == new_device); + } + + SECTION("Context Upload Command Kit") + { + Rhi::CommandKit upload_cmd_kit; + REQUIRE_NOTHROW(upload_cmd_kit = compute_context.GetUploadCommandKit()); + REQUIRE(upload_cmd_kit.IsInitialized()); + CHECK(upload_cmd_kit.GetListType() == Rhi::CommandListType::Transfer); + } + + SECTION("Context Compute Command Kit") + { + Rhi::CommandKit compute_cmd_kit; + REQUIRE_NOTHROW(compute_cmd_kit = compute_context.GetComputeCommandKit()); + REQUIRE(compute_cmd_kit.IsInitialized()); + CHECK(compute_cmd_kit.GetListType() == Rhi::CommandListType::Compute); + } + + SECTION("Context Compute Command Kit") + { + Rhi::CommandKit compute_cmd_kit; + REQUIRE_NOTHROW(compute_cmd_kit = compute_context.GetComputeCommandKit()); + REQUIRE(compute_cmd_kit.IsInitialized()); + CHECK(compute_cmd_kit.GetListType() == Rhi::CommandListType::Compute); + } + + SECTION("Context Default Command Kits") + { + const std::array cmd_list_types { Rhi::CommandListType::Compute, Rhi::CommandListType::Transfer }; + for(Rhi::CommandListType cmd_list_type : cmd_list_types) + { + Rhi::CommandKit default_cmd_kit = compute_context.GetDefaultCommandKit(cmd_list_type); + CHECK(default_cmd_kit.IsInitialized()); + CHECK(default_cmd_kit.GetListType() == cmd_list_type); + } + } + + SECTION("Context Upload Resources") + { + Rhi::TransferCommandList transfer_cmd_list = compute_context.GetUploadCommandKit().GetTransferListForEncoding(); + CHECK(transfer_cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK_NOTHROW(compute_context.UploadResources()); + CHECK(transfer_cmd_list.GetState() == Rhi::CommandListState::Executing); + } + + SECTION("Context Upload Resources Deferred") + { + Rhi::TransferCommandList transfer_cmd_list = compute_context.GetUploadCommandKit().GetTransferListForEncoding(); + CHECK(transfer_cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK_NOTHROW(compute_context.RequestDeferredAction(Rhi::ContextDeferredAction::UploadResources)); + CHECK_NOTHROW(compute_context.WaitForGpu(Rhi::ContextWaitFor::ComputeComplete)); + //FIXME: CHECK(transfer_cmd_list.GetState() == Rhi::CommandListState::Executing); + } + + SECTION("Context Complete Initialization") + { + ContextCallbackTester context_callback_tester(compute_context); + REQUIRE_NOTHROW(compute_context.CompleteInitialization()); + CHECK(context_callback_tester.IsContextCompletingInitialization()); + CHECK_FALSE(compute_context.IsCompletingInitialization()); + } + + SECTION("Context Complete Initialization Deferred") + { + ContextCallbackTester context_callback_tester(compute_context); + Rhi::TransferCommandList transfer_cmd_list = compute_context.GetUploadCommandKit().GetTransferListForEncoding(); + CHECK(transfer_cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK_NOTHROW(compute_context.RequestDeferredAction(Rhi::ContextDeferredAction::CompleteInitialization)); + CHECK_NOTHROW(compute_context.WaitForGpu(Rhi::ContextWaitFor::ComputeComplete)); + //FIXME: CHECK(transfer_cmd_list.GetState() == Rhi::CommandListState::Executing); + } +} + +TEST_CASE("RHI Compute Context Factory", "[rhi][compute][context][factory]") +{ + const Rhi::ComputeContext compute_context(GetTestDevice(), g_parallel_executor, {}); + + SECTION("Can Create Compute Command Queue") + { + Rhi::CommandQueue command_queue; + REQUIRE_NOTHROW(command_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Compute)); + REQUIRE(command_queue.IsInitialized()); + CHECK(command_queue.GetCommandListType() == Rhi::CommandListType::Compute); + } + + SECTION("Can Create Transfer Command Queue") + { + Rhi::CommandQueue command_queue; + REQUIRE_NOTHROW(command_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Transfer)); + REQUIRE(command_queue.IsInitialized()); + CHECK(command_queue.GetCommandListType() == Rhi::CommandListType::Transfer); + } + + SECTION("Can not Create Render Command Queue") + { + Rhi::CommandQueue command_queue; + REQUIRE_THROWS(command_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Render)); + } + + SECTION("Can Create Compute Command Kit") + { + Rhi::CommandKit command_kit; + REQUIRE_NOTHROW(command_kit = compute_context.CreateCommandKit(Rhi::CommandListType::Compute)); + REQUIRE(command_kit.IsInitialized()); + CHECK(command_kit.GetListType() == Rhi::CommandListType::Compute); + } + + SECTION("Can Create Transfer Command Kit") + { + Rhi::CommandKit command_kit; + REQUIRE_NOTHROW(command_kit = compute_context.CreateCommandKit(Rhi::CommandListType::Transfer)); + REQUIRE(command_kit.IsInitialized()); + CHECK(command_kit.GetListType() == Rhi::CommandListType::Transfer); + } + + SECTION("Can not Create Render Command Kit") + { + Rhi::CommandKit command_kit; + REQUIRE_THROWS(command_kit = compute_context.CreateCommandKit(Rhi::CommandListType::Render)); + } + + SECTION("Can Create Shader") + { + const Rhi::ShaderSettings shader_settings{ Data::ShaderProvider::Get(), { "Shader", "Main" } }; + Rhi::Shader shader; + REQUIRE_NOTHROW(shader = compute_context.CreateShader(Rhi::ShaderType::Compute, shader_settings)); + REQUIRE(shader.IsInitialized()); + CHECK(shader.GetType() == Rhi::ShaderType::Compute); + CHECK(shader.GetSettings().entry_function == shader_settings.entry_function); + } + + SECTION("Can Create Program") + { + Rhi::Program program; + REQUIRE_NOTHROW(program = compute_context.CreateProgram({ + { { Rhi::ShaderType::Compute, { Data::ShaderProvider::Get(), { "Shader", "Main" } } } }, + })); + REQUIRE(program.IsInitialized()); + CHECK(program.GetSettings().shaders.size() == 1); + CHECK(program.GetShader(Rhi::ShaderType::Compute).GetSettings().entry_function == Rhi::ShaderEntryFunction{ "Shader", "Main" }); + } + + SECTION("Can Create Compute State") + { + const Rhi::ComputeStateSettingsImpl& compute_state_settings{ + compute_context.CreateProgram({ + { { Rhi::ShaderType::Compute, { Data::ShaderProvider::Get(), { "Shader", "Main" } } } }, + }), + Rhi::ThreadGroupSize(16, 16, 1) + }; + Rhi::ComputeState compute_state; + REQUIRE_NOTHROW(compute_state = compute_context.CreateComputeState(compute_state_settings)); + REQUIRE(compute_state.IsInitialized()); + CHECK(compute_state.GetSettings().program_ptr == compute_state_settings.program.GetInterfacePtr()); + CHECK(compute_state.GetSettings().thread_group_size == compute_state_settings.thread_group_size); + } + + SECTION("Can Create Buffer") + { + const Rhi::BufferSettings buffer_settings = Rhi::BufferSettings::ForConstantBuffer(42); + Rhi::Buffer buffer; + REQUIRE_NOTHROW(buffer = compute_context.CreateBuffer(buffer_settings)); + REQUIRE(buffer.IsInitialized()); + CHECK(buffer.GetSettings().type == Rhi::BufferType::Constant); + CHECK(buffer.GetSettings().size == buffer_settings.size); + } + + SECTION("Can Create Texture") + { + const Rhi::TextureSettings buffer_settings = Rhi::TextureSettings::ForImage(Dimensions(640, 480), 2, PixelFormat::RGBA8Unorm_sRGB, true); + Rhi::Texture texture; + REQUIRE_NOTHROW(texture = compute_context.CreateTexture(buffer_settings)); + REQUIRE(texture.IsInitialized()); + CHECK(texture.GetSettings().type == Rhi::TextureType::Image); + CHECK(texture.GetSettings().dimension_type == Rhi::TextureDimensionType::Tex2DArray); + CHECK(texture.GetSettings().array_length == 2); + CHECK(texture.GetSettings().dimensions == Dimensions(640, 480)); + CHECK(texture.GetSettings().pixel_format == PixelFormat::RGBA8Unorm_sRGB); + CHECK(texture.GetSettings().mipmapped); + } + + SECTION("Can Create Sampler") + { + const Rhi::SamplerSettings sampler_settings{ + rhi::SamplerFilter { rhi::SamplerFilter::MinMag::Linear }, + rhi::SamplerAddress { rhi::SamplerAddress::Mode::ClampToEdge } + }; + Rhi::Sampler sampler; + REQUIRE_NOTHROW(sampler = compute_context.CreateSampler(sampler_settings)); + REQUIRE(sampler.IsInitialized()); + CHECK(sampler.GetSettings().filter.min == Rhi::SamplerFilter::MinMag::Linear); + CHECK(sampler.GetSettings().filter.mag == Rhi::SamplerFilter::MinMag::Linear); + CHECK(sampler.GetSettings().address.r == Rhi::SamplerAddress::Mode::ClampToEdge); + CHECK(sampler.GetSettings().address.s == Rhi::SamplerAddress::Mode::ClampToEdge); + CHECK(sampler.GetSettings().address.t == Rhi::SamplerAddress::Mode::ClampToEdge); + } + + SECTION("Can Get Object Registry") + { + Rhi::IObjectRegistry* registry_ptr = nullptr; + REQUIRE_NOTHROW(registry_ptr = &compute_context.GetObjectRegistry()); + REQUIRE(registry_ptr); + CHECK_FALSE(registry_ptr->HasGraphicsObject("Something")); + } + + SECTION("Can Get Parallel Executor") + { + tf::Executor* executor_ptr = nullptr; + REQUIRE_NOTHROW(executor_ptr = &compute_context.GetParallelExecutor()); + REQUIRE(executor_ptr); + CHECK(executor_ptr->num_workers() > 0); + } +} diff --git a/Tests/Graphics/RHI/ComputeStateTest.cpp b/Tests/Graphics/RHI/ComputeStateTest.cpp new file mode 100644 index 000000000..9b7612f80 --- /dev/null +++ b/Tests/Graphics/RHI/ComputeStateTest.cpp @@ -0,0 +1,115 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/ComputeStateTest.cpp +Unit-tests of the RHI ComputeContext + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Compute State Functions", "[rhi][compute][state]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + const Rhi::ComputeStateSettingsImpl& compute_state_settings{ + compute_context.CreateProgram({ + { { Rhi::ShaderType::Compute, { Data::ShaderProvider::Get(), { "Shader", "Main" } } } }, + }), + Rhi::ThreadGroupSize(16, 16, 1) + }; + + SECTION("Context Construction") + { + Rhi::ComputeState compute_state; + REQUIRE_NOTHROW(compute_state = compute_context.CreateComputeState(compute_state_settings)); + REQUIRE(compute_state.IsInitialized()); + CHECK(compute_state.GetInterfacePtr()); + CHECK(compute_state.GetSettings().thread_group_size == compute_state_settings.thread_group_size); + CHECK(compute_state.GetProgram().GetInterfacePtr().get() == compute_state_settings.program.GetInterfacePtr().get()); + } + + SECTION("Object Destroyed Callback") + { + auto compute_state_ptr = std::make_unique(compute_context, compute_state_settings); + ObjectCallbackTester object_callback_tester(*compute_state_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + compute_state_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + const Rhi::ComputeState compute_state = compute_context.CreateComputeState(compute_state_settings); + + SECTION("Object Name Setup") + { + CHECK(compute_state.SetName("My Compute State")); + CHECK(compute_state.GetName() == "My Compute State"); + } + + SECTION("Object Name Change Callback") + { + CHECK(compute_state.SetName("My Compute State")); + ObjectCallbackTester object_callback_tester(compute_state); + CHECK(compute_state.SetName("Our Compute State")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Compute State"); + CHECK(object_callback_tester.GetOldObjectName() == "My Compute State"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(compute_state.SetName("My Compute State")); + ObjectCallbackTester object_callback_tester(compute_state); + CHECK_FALSE(compute_state.SetName("My Compute State")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Reset with Settings Impl") + { + const Rhi::Program new_compute_program = compute_context.CreateProgram({ + { { Rhi::ShaderType::Compute, { Data::ShaderProvider::Get(), { "Compute", "New" } } } }, + }); + REQUIRE_NOTHROW(compute_state.Reset(Rhi::ComputeStateSettingsImpl{ new_compute_program, Rhi::ThreadGroupSize(32, 32, 1) })); + REQUIRE(compute_state.GetProgram().GetInterfacePtr().get() == new_compute_program.GetInterfacePtr().get()); + REQUIRE(compute_state.GetSettings().thread_group_size == Rhi::ThreadGroupSize(32, 32, 1)); + } + + SECTION("Reset with Settings") + { + const Rhi::Program new_compute_program = compute_context.CreateProgram({ + { { Rhi::ShaderType::Compute, { Data::ShaderProvider::Get(), { "Compute", "New" } } } }, + }); + REQUIRE_NOTHROW(compute_state.Reset(Rhi::ComputeStateSettings{ new_compute_program.GetInterfacePtr(), Rhi::ThreadGroupSize(32, 32, 1) })); + REQUIRE(compute_state.GetProgram().GetInterfacePtr().get() == new_compute_program.GetInterfacePtr().get()); + REQUIRE(compute_state.GetSettings().thread_group_size == Rhi::ThreadGroupSize(32, 32, 1)); + } +} diff --git a/Tests/Graphics/RHI/FenceTest.cpp b/Tests/Graphics/RHI/FenceTest.cpp new file mode 100644 index 000000000..760730a1c --- /dev/null +++ b/Tests/Graphics/RHI/FenceTest.cpp @@ -0,0 +1,113 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/FenceTest.cpp +Unit-tests of the RHI Fence + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Fence Functions", "[rhi][queue]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + const Rhi::CommandQueue compute_cmd_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Compute); + + SECTION("Fence Construction") + { + Rhi::Fence fence; + REQUIRE_NOTHROW(fence = compute_cmd_queue.CreateFence()); + REQUIRE(fence.IsInitialized()); + CHECK(fence.GetInterfacePtr()); + } + + SECTION("Object Destroyed Callback") + { + auto fence_ptr = std::make_unique(compute_cmd_queue); + ObjectCallbackTester object_callback_tester(*fence_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + fence_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + const Rhi::Fence fence = compute_cmd_queue.CreateFence(); + + SECTION("Object Name Setup") + { + CHECK(fence.SetName("My Fence")); + CHECK(fence.GetName() == "My Fence"); + } + + SECTION("Object Name Change Callback") + { + CHECK(fence.SetName("My Fence")); + ObjectCallbackTester object_callback_tester(fence); + CHECK(fence.SetName("Our Fence")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Fence"); + CHECK(object_callback_tester.GetOldObjectName() == "My Fence"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(fence.SetName("My Fence")); + ObjectCallbackTester object_callback_tester(fence); + CHECK_FALSE(fence.SetName("My Fence")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Signal Fence") + { + CHECK_NOTHROW(fence.Signal()); + } + + SECTION("Wait on CPU") + { + CHECK_NOTHROW(fence.WaitOnCpu()); + } + + SECTION("Wait on GPU") + { + CHECK_NOTHROW(fence.WaitOnGpu(compute_context.GetUploadCommandKit().GetQueue())); + } + + SECTION("Flush on CPU") + { + CHECK_NOTHROW(fence.FlushOnCpu()); + } + + SECTION("Flush on GPU") + { + CHECK_NOTHROW(fence.FlushOnGpu(compute_context.GetUploadCommandKit().GetQueue())); + } +} diff --git a/Tests/Graphics/RHI/ProgramBindingsTest.cpp b/Tests/Graphics/RHI/ProgramBindingsTest.cpp new file mode 100644 index 000000000..5749ff42f --- /dev/null +++ b/Tests/Graphics/RHI/ProgramBindingsTest.cpp @@ -0,0 +1,268 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/ProgramBindingsTest.cpp +Unit-tests of the RHI Program Bindings + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Program Bindings Functions", "[rhi][program][bindings]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + const Rhi::Program compute_program = [&compute_context]() + { + const Rhi::ProgramArgumentAccessor texture_accessor{ Rhi::ShaderType::Compute, "InTexture", Rhi::ProgramArgumentAccessType::Constant }; + const Rhi::ProgramArgumentAccessor sampler_accessor{ Rhi::ShaderType::Compute, "InSampler", Rhi::ProgramArgumentAccessType::Constant }; + const Rhi::ProgramArgumentAccessor buffer_accessor { Rhi::ShaderType::Compute, "OutBuffer", Rhi::ProgramArgumentAccessType::Mutable }; + Rhi::Program compute_program = compute_context.CreateProgram( + Rhi::ProgramSettingsImpl + { + Rhi::ProgramSettingsImpl::ShaderSet + { + { Rhi::ShaderType::Compute, { Data::ShaderProvider::Get(), { "Compute", "Main" } } } + }, + Rhi::ProgramInputBufferLayouts{ }, + Rhi::ProgramArgumentAccessors + { + texture_accessor, + sampler_accessor, + buffer_accessor + } + }); + dynamic_cast(compute_program.GetInterface()).SetArgumentBindings({ + { texture_accessor, { Rhi::ResourceType::Texture, 1U } }, + { sampler_accessor, { Rhi::ResourceType::Sampler, 1U } }, + { buffer_accessor, { Rhi::ResourceType::Buffer, 1U } }, + }); + return compute_program; + }(); + + const Rhi::Texture texture = [&compute_context]() + { + Rhi::Texture texture = compute_context.CreateTexture(Rhi::TextureSettings::ForImage(Dimensions(640, 480), {}, PixelFormat::RGBA8, false)); + texture.SetName("T"); + return texture; + }(); + + const Rhi::Sampler sampler = [&compute_context]() + { + const Rhi::Sampler sampler = compute_context.CreateSampler({ + rhi::SamplerFilter { rhi::SamplerFilter::MinMag::Linear }, + rhi::SamplerAddress { rhi::SamplerAddress::Mode::ClampToEdge } + }); + sampler.SetName("S"); + return sampler; + }(); + + const Rhi::Buffer buffer1 = [&compute_context]() + { + const Rhi::Buffer buffer = compute_context.CreateBuffer(Rhi::BufferSettings::ForConstantBuffer(42000, false, true)); + buffer.SetName("B1"); + return buffer; + }(); + + const Rhi::Buffer buffer2 = [&compute_context]() + { + const Rhi::Buffer buffer = compute_context.CreateBuffer(Rhi::BufferSettings::ForConstantBuffer(64000, false, true)); + buffer.SetName("B2"); + return buffer; + }(); + + const Rhi::Program::ResourceViewsByArgument compute_resource_views{ + { { Rhi::ShaderType::Compute, "InTexture" }, { { texture.GetInterface() } } }, + { { Rhi::ShaderType::Compute, "InSampler" }, { { sampler.GetInterface() } } }, + { { Rhi::ShaderType::Compute, "OutBuffer" }, { { buffer1.GetInterface() } } }, + }; + + SECTION("Create Compute Program Bindings") + { + Rhi::ProgramBindings program_bindings; + REQUIRE_NOTHROW(program_bindings = compute_program.CreateBindings(compute_resource_views, 2U)); + REQUIRE(program_bindings.IsInitialized()); + CHECK(program_bindings.GetInterfacePtr()); + CHECK(program_bindings.GetArguments().size() == 3U); + CHECK(program_bindings.GetFrameIndex() == 2U); + for(const auto& [program_argument, argument_resource_views] : compute_resource_views) + { + const Rhi::ResourceViews& program_resource_views = program_bindings.Get(program_argument).GetResourceViews(); + CHECK_FALSE(argument_resource_views.empty()); + CHECK(program_resource_views.size() == argument_resource_views.size()); + CHECK(program_resource_views.at(0).GetResourcePtr().get() == argument_resource_views.at(0).GetResourcePtr().get()); + } + } + + SECTION("Can not create Compute Program Bindings with Unbound Resources") + { + Rhi::ProgramBindings program_bindings; + REQUIRE_THROWS_AS(program_bindings = compute_program.CreateBindings({ + { { Rhi::ShaderType::Compute, "InTexture" }, { { texture.GetInterface() } } }, + { { Rhi::ShaderType::Compute, "OutBuffer" }, { { buffer1.GetInterface() } } } + }), + Rhi::ProgramBindingsUnboundArgumentsException); + } + + SECTION("Create Multiple Compute Program Bindings") + { + std::vector program_bindings; + for(size_t i = 0; i < 10; ++i) + { + REQUIRE_NOTHROW(program_bindings.push_back(compute_program.CreateBindings(compute_resource_views))); + REQUIRE(program_bindings.back().IsInitialized()); + CHECK(program_bindings.back().GetArguments().size() == 3U); + CHECK(program_bindings.back().GetBindingsIndex() == i); + } + CHECK(compute_program.GetBindingsCount() == 10); + program_bindings.clear(); + // FIXME: CHECK(compute_program.GetBindingsCount() == 0); + } + + SECTION("Create A Copy of Program Bindings with Replacements") + { + Rhi::ProgramBindings orig_program_bindings = compute_program.CreateBindings(compute_resource_views, 2U); + Rhi::ProgramBindings copy_program_bindings; + REQUIRE_NOTHROW(copy_program_bindings = Rhi::ProgramBindings(orig_program_bindings, { + { { Rhi::ShaderType::Compute, "OutBuffer" }, { { buffer2.GetInterface() } } }, + }, 3U)); + REQUIRE(copy_program_bindings.IsInitialized()); + CHECK(copy_program_bindings.GetInterfacePtr()); + CHECK(copy_program_bindings.GetArguments().size() == 3U); + CHECK(copy_program_bindings.GetFrameIndex() == 3U); + CHECK(copy_program_bindings.Get({ Rhi::ShaderType::Compute, "InTexture" }).GetResourceViews().at(0).GetResourcePtr().get() == texture.GetInterfacePtr().get()); + CHECK(copy_program_bindings.Get({ Rhi::ShaderType::Compute, "InSampler" }).GetResourceViews().at(0).GetResourcePtr().get() == sampler.GetInterfacePtr().get()); + CHECK(copy_program_bindings.Get({ Rhi::ShaderType::Compute, "OutBuffer" }).GetResourceViews().at(0).GetResourcePtr().get() == buffer2.GetInterfacePtr().get()); + } + + SECTION("Object Destroyed Callback") + { + auto program_bindings_ptr = std::make_unique(compute_program, compute_resource_views); + ObjectCallbackTester object_callback_tester(*program_bindings_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + program_bindings_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + const Rhi::ProgramBindings program_bindings = compute_program.CreateBindings(compute_resource_views); + + SECTION("Object Name Setup") + { + CHECK(program_bindings.SetName("My Program Bindings")); + CHECK(program_bindings.GetName() == "My Program Bindings"); + } + + SECTION("Object Name Change Callback") + { + CHECK(program_bindings.SetName("My Program Bindings")); + ObjectCallbackTester object_callback_tester(program_bindings); + CHECK(program_bindings.SetName("Our Program Bindings")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Program Bindings"); + CHECK(object_callback_tester.GetOldObjectName() == "My Program Bindings"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(program_bindings.SetName("My Program Bindings")); + ObjectCallbackTester object_callback_tester(program_bindings); + CHECK_FALSE(program_bindings.SetName("My Program Bindings")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Can Get Program Binding Arguments") + { + Rhi::ProgramArguments program_arguments; + REQUIRE(program_bindings.GetArguments().size() == 3U); + REQUIRE_NOTHROW(program_arguments = program_bindings.GetArguments()); + CHECK(program_arguments.count({ Rhi::ShaderType::Compute, "InTexture" }) == 1); + CHECK(program_arguments.count({ Rhi::ShaderType::Compute, "InSampler" }) == 1); + CHECK(program_arguments.count({ Rhi::ShaderType::Compute, "OutBuffer" }) == 1); + } + + SECTION("Can Get Texture Argument Binding") + { + Rhi::IProgramArgumentBinding* texture_binding_ptr = nullptr; + REQUIRE_NOTHROW(texture_binding_ptr = &program_bindings.Get({ Rhi::ShaderType::Compute, "InTexture" })); + REQUIRE(texture_binding_ptr); + CHECK(texture_binding_ptr->GetSettings().argument.GetName() == "InTexture"); + CHECK(texture_binding_ptr->GetSettings().resource_count == 1U); + CHECK(texture_binding_ptr->GetSettings().resource_type == Rhi::ResourceType::Texture); + CHECK(texture_binding_ptr->GetResourceViews().size() == 1U); + CHECK(texture_binding_ptr->GetResourceViews().at(0).GetResourcePtr().get() == texture.GetInterfacePtr().get()); + } + + SECTION("Can Get Sampler Argument Binding") + { + Rhi::IProgramArgumentBinding* sampler_binding_ptr = nullptr; + REQUIRE_NOTHROW(sampler_binding_ptr = &program_bindings.Get({ Rhi::ShaderType::Compute, "InSampler" })); + REQUIRE(sampler_binding_ptr); + CHECK(sampler_binding_ptr->GetSettings().argument.GetName() == "InSampler"); + CHECK(sampler_binding_ptr->GetSettings().resource_count == 1U); + CHECK(sampler_binding_ptr->GetSettings().resource_type == Rhi::ResourceType::Sampler); + CHECK(sampler_binding_ptr->GetResourceViews().size() == 1U); + CHECK(sampler_binding_ptr->GetResourceViews().at(0).GetResourcePtr().get() == sampler.GetInterfacePtr().get()); + } + + SECTION("Can Get Buffer Argument Binding") + { + Rhi::IProgramArgumentBinding* buffer_binding_ptr = nullptr; + REQUIRE_NOTHROW(buffer_binding_ptr = &program_bindings.Get({ Rhi::ShaderType::Compute, "OutBuffer" })); + REQUIRE(buffer_binding_ptr); + CHECK(buffer_binding_ptr->GetSettings().argument.GetName() == "OutBuffer"); + CHECK(buffer_binding_ptr->GetSettings().resource_count == 1U); + CHECK(buffer_binding_ptr->GetSettings().resource_type == Rhi::ResourceType::Buffer); + CHECK(buffer_binding_ptr->GetResourceViews().size() == 1U); + CHECK(buffer_binding_ptr->GetResourceViews().at(0).GetResourcePtr().get() == buffer1.GetInterfacePtr().get()); + } + + SECTION("Can Change Buffer Argument Binding") + { + Rhi::IProgramArgumentBinding& buffer_binding = program_bindings.Get({ Rhi::ShaderType::Compute, "OutBuffer" }); + REQUIRE_NOTHROW(buffer_binding.SetResourceViews({ { buffer2.GetInterface(), 0U, 128U } })); + const Rhi::ResourceView& buffer_view = buffer_binding.GetResourceViews().at(0); + CHECK(buffer_view.GetResourcePtr().get() == buffer2.GetInterfacePtr().get()); + CHECK(buffer_view.GetOffset() == 0U); + CHECK(buffer_view.GetSize() == 128U); + } + + SECTION("Convert to String") + { + CHECK(static_cast(program_bindings) == + " - Compute shaders argument 'InSampler' (Constant) is bound to Sampler 'S' subresources from index(d:0, a:0, m:0) for count(d:0, a:0, m:0) with offset 0;\n" \ + " - Compute shaders argument 'InTexture' (Constant) is bound to Texture 'T' subresources from index(d:0, a:0, m:0) for count(d:1, a:1, m:1) with offset 0;\n" \ + " - Compute shaders argument 'OutBuffer' (Mutable) is bound to Buffer 'B1' subresources from index(d:0, a:0, m:0) for count(d:1, a:1, m:1) with offset 0."); + } +} \ No newline at end of file diff --git a/Tests/Graphics/RHI/ProgramTest.cpp b/Tests/Graphics/RHI/ProgramTest.cpp new file mode 100644 index 000000000..becfc9563 --- /dev/null +++ b/Tests/Graphics/RHI/ProgramTest.cpp @@ -0,0 +1,132 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/ProgramTest.cpp +Unit-tests of the RHI Program + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +void CheckShaderSettings(const Methane::Ptrs& shader_ptrs, const Rhi::ProgramSettingsImpl::ShaderSet& shader_settings) +{ + for(const auto& shader_ptr : shader_ptrs) + { + REQUIRE(shader_ptr); + const auto shader_it = shader_settings.find(shader_ptr->GetType()); + REQUIRE(shader_it != shader_settings.end()); + CHECK(shader_ptr->GetSettings() == shader_it->second); + } +} + +const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); +const Rhi::ProgramSettingsImpl compute_program_settings{ + { { Rhi::ShaderType::Compute, { Data::ShaderProvider::Get(), { "Compute", "Main" } } } }, +}; + +TEST_CASE("RHI Program Functions", "[rhi][program]") +{ + SECTION("Compute Program Construction") + { + Rhi::Program compute_program; + REQUIRE_NOTHROW(compute_program = compute_context.CreateProgram(compute_program_settings)); + REQUIRE(compute_program.IsInitialized()); + CHECK(compute_program.GetInterfacePtr()); + CheckShaderSettings(compute_program.GetSettings().shaders, compute_program_settings.shader_set); + } + + SECTION("Object Destroyed Callback") + { + auto program_ptr = std::make_unique(compute_context, compute_program_settings); + ObjectCallbackTester object_callback_tester(*program_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + program_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + const Rhi::Program compute_program = compute_context.CreateProgram(compute_program_settings); + + SECTION("Object Name Setup") + { + CHECK(compute_program.SetName("My Program")); + CHECK(compute_program.GetName() == "My Program"); + } + + SECTION("Object Name Change Callback") + { + CHECK(compute_program.SetName("My Program")); + ObjectCallbackTester object_callback_tester(compute_program); + CHECK(compute_program.SetName("Our Program")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Program"); + CHECK(object_callback_tester.GetOldObjectName() == "My Program"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(compute_program.SetName("My Program")); + ObjectCallbackTester object_callback_tester(compute_program); + CHECK_FALSE(compute_program.SetName("My Program")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Can Get Shader Types") + { + CHECK(compute_program.GetShaderTypes() == Rhi::ShaderTypes{ Rhi::ShaderType::Compute }); + } + + SECTION("Can Get Existing Shader By Type") + { + Rhi::Shader compute_shader; + REQUIRE_NOTHROW(compute_shader = compute_program.GetShader(Rhi::ShaderType::Compute)); + CHECK(compute_shader.GetType() == Rhi::ShaderType::Compute); + CHECK(compute_shader.GetSettings() == compute_program_settings.shader_set.at(Rhi::ShaderType::Compute)); + } +} + +TEST_CASE("RHI Program Factory", "[rhi][program][factory]") +{ + const Rhi::Program compute_program = compute_context.CreateProgram(Rhi::ProgramSettingsImpl{ + { { Rhi::ShaderType::Compute, { Data::ShaderProvider::Get(), { "Compute", "Main" } } } }, + }); + + SECTION("Can Create Program Bindings") + { + Rhi::ProgramBindings program_bindings; + CHECK(compute_program.GetBindingsCount() == 0); + REQUIRE_NOTHROW(program_bindings = compute_program.CreateBindings({ })); + CHECK(compute_program.GetBindingsCount() == 1); + REQUIRE(program_bindings.IsInitialized()); + CHECK(program_bindings.GetInterfacePtr()); + } +} \ No newline at end of file diff --git a/Tests/Graphics/RHI/RhiTestHelpers.hpp b/Tests/Graphics/RHI/RhiTestHelpers.hpp new file mode 100644 index 000000000..789c167ba --- /dev/null +++ b/Tests/Graphics/RHI/RhiTestHelpers.hpp @@ -0,0 +1,233 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/RhiTestHelpers.hpp +RHI Test helper classes + +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Methane +{ + +namespace rhi = Methane::Graphics::Rhi; + +static rhi::Device GetTestDevice() +{ + static const rhi::Devices& devices = rhi::System::Get().UpdateGpuDevices(); + if (devices.empty()) + throw std::logic_error("No RHI devices available"); + + return devices[0]; +} + +class ObjectCallbackTester final + : private Data::Receiver +{ +public: + ObjectCallbackTester(rhi::IObject& obj) + : m_obj(obj) + { obj.Connect(*this); } + + template + ObjectCallbackTester(ObjectType& obj) + : m_obj(obj.GetInterface()) + { obj.Connect(*this); } + + bool IsObjectDestroyed() const noexcept + { return m_is_object_destroyed; } + + bool IsObjectNameChanged() const noexcept + { return m_is_object_name_changed; } + + const std::string& GetOldObjectName() const noexcept + { return m_old_name; } + + const std::string& GetCurObjectName() const noexcept + { return m_cur_name; } + + void ResetObjectNameChanged() + { m_is_object_name_changed = false; } + +private: + void OnObjectNameChanged(rhi::IObject& obj, const std::string& old_name) override + { + CHECK(std::addressof(obj) == std::addressof(m_obj)); + m_is_object_name_changed = true; + m_old_name = old_name; + m_cur_name = obj.GetName(); + } + + void OnObjectDestroyed(rhi::IObject& obj) override + { + CHECK(std::addressof(obj) == std::addressof(m_obj)); + m_is_object_destroyed = true; + } + + rhi::IObject& m_obj; + bool m_is_object_destroyed = false; + bool m_is_object_name_changed = false; + std::string m_old_name; + std::string m_cur_name; +}; + +class ContextCallbackTester final + : private Data::Receiver +{ +public: + ContextCallbackTester(rhi::IContext& context) + : m_context(context) + { dynamic_cast&>(context).Connect(*this); } + + template + ContextCallbackTester(ContextType& context) + : m_context(context.GetInterface()) + { context.Connect(*this); } + + bool IsContextReleased() const noexcept + { return m_is_context_released; } + + bool IsContextCompletingInitialization() const noexcept + { return m_is_context_completing_initialization; } + + bool IsContextInitialized() const noexcept + { return m_is_context_initialized; } + + void Reset() + { + m_is_context_released = false; + m_is_context_completing_initialization = false; + m_is_context_initialized = false; + } + +private: + void OnContextReleased(rhi::IContext& context) override + { + CHECK(std::addressof(m_context) == std::addressof(context)); + m_is_context_released = true; + } + + void OnContextCompletingInitialization(rhi::IContext& context) override + { + CHECK(std::addressof(m_context) == std::addressof(context)); + CHECK(context.IsCompletingInitialization()); + m_is_context_completing_initialization = true; + } + + void OnContextInitialized(rhi::IContext& context) override + { + CHECK(std::addressof(m_context) == std::addressof(context)); + m_is_context_initialized = true; + } + + rhi::IContext& m_context; + bool m_is_context_released = false; + bool m_is_context_completing_initialization = false; + bool m_is_context_initialized = false; +}; + +class CommandListCallbackTester final + : private Data::Receiver +{ +public: + CommandListCallbackTester(rhi::ICommandList& cmd_list) + : m_cmd_list(cmd_list) + { dynamic_cast&>(cmd_list).Connect(*this); } + + template + CommandListCallbackTester(CommandListType& cmd_list) + : m_cmd_list(cmd_list.GetInterface()) + { cmd_list.Connect(*this); } + + bool IsStateChanged() const noexcept { return m_is_state_changed; } + bool IsExecutionCompleted() const noexcept { return m_is_execution_completed; } + rhi::CommandListState GetTrackingState() const noexcept { return m_state; } + + void Reset() + { + m_is_state_changed = false; + m_is_execution_completed = false; + m_state = rhi::CommandListState::Pending; + } + +private: + void OnCommandListStateChanged(rhi::ICommandList& cmd_list) override + { + CHECK(std::addressof(cmd_list) == std::addressof(m_cmd_list)); + m_is_state_changed = true; + m_state = cmd_list.GetState(); + } + + void OnCommandListExecutionCompleted(rhi::ICommandList& cmd_list) override + { + CHECK(std::addressof(cmd_list) == std::addressof(m_cmd_list)); + CHECK(cmd_list.GetState() == rhi::CommandListState::Pending); + m_is_execution_completed = true; + m_state = cmd_list.GetState(); + } + + rhi::ICommandList& m_cmd_list; + bool m_is_state_changed = false; + bool m_is_execution_completed = false; + rhi::CommandListState m_state = rhi::CommandListState::Pending; +}; + +class ResourceCallbackTester final + : private Data::Receiver +{ +public: + ResourceCallbackTester(rhi::IResource& resource) + : m_resource(resource) + { dynamic_cast&>(resource).Connect(*this); } + + template + ResourceCallbackTester(ResourceType& resource) + : m_resource(resource.GetInterface()) + { resource.Connect(*this); } + + bool IsResourceReleased() const noexcept { return m_is_resource_released; } + + void Reset() + { + m_is_resource_released = false; + } + +private: + void OnResourceReleased(rhi::IResource& resource) override + { + CHECK(std::addressof(resource) == std::addressof(m_resource)); + m_is_resource_released = true; + } + + rhi::IResource& m_resource; + bool m_is_resource_released = false; +}; + +} // namespace Methane \ No newline at end of file diff --git a/Tests/Graphics/RHI/SamplerTest.cpp b/Tests/Graphics/RHI/SamplerTest.cpp new file mode 100644 index 000000000..8c5c7eaae --- /dev/null +++ b/Tests/Graphics/RHI/SamplerTest.cpp @@ -0,0 +1,151 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/SamplerTest.cpp +Unit-tests of the RHI Sampler + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Sampler Functions", "[rhi][sampler][resource]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + const Rhi::SamplerSettings sampler_settings{ + rhi::SamplerFilter { rhi::SamplerFilter::MinMag::Linear }, + rhi::SamplerAddress { rhi::SamplerAddress::Mode::ClampToEdge }, + rhi::SamplerLevelOfDetail{ 0.5f, 0.f, 1.f }, + 2U, rhi::SamplerBorderColor::OpaqueBlack, + Compare::GreaterEqual + }; + + SECTION("Constant Sampler Construction") + { + Rhi::Sampler sampler; + REQUIRE_NOTHROW(sampler = compute_context.CreateSampler(sampler_settings)); + REQUIRE(sampler.IsInitialized()); + CHECK(sampler.GetInterfacePtr()); + CHECK(sampler.GetResourceType() == Rhi::ResourceType::Sampler); + CHECK(sampler.GetSettings() == sampler_settings); + CHECK(std::addressof(sampler.GetContext()) == compute_context.GetInterfacePtr().get()); + } + + SECTION("Object Destroyed Callback") + { + auto sampler_ptr = std::make_unique(compute_context, sampler_settings); + ObjectCallbackTester object_callback_tester(*sampler_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + sampler_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + SECTION("Resource Released Callback") + { + auto sampler_ptr = std::make_unique(compute_context, sampler_settings); + ResourceCallbackTester resource_callback_tester(*sampler_ptr); + CHECK_FALSE(resource_callback_tester.IsResourceReleased()); + sampler_ptr.reset(); + CHECK(resource_callback_tester.IsResourceReleased()); + } + + const Rhi::Sampler sampler = compute_context.CreateSampler(sampler_settings); + + SECTION("Object Name Setup") + { + CHECK(sampler.SetName("My Sampler")); + CHECK(sampler.GetName() == "My Sampler"); + } + + SECTION("Object Name Change Callback") + { + CHECK(sampler.SetName("My Sampler")); + ObjectCallbackTester object_callback_tester(sampler); + CHECK(sampler.SetName("Our Sampler")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Sampler"); + CHECK(object_callback_tester.GetOldObjectName() == "My Sampler"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(sampler.SetName("My Sampler")); + ObjectCallbackTester object_callback_tester(sampler); + CHECK_FALSE(sampler.SetName("My Sampler")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Set State") + { + CHECK(sampler.GetState() == Rhi::ResourceState::Undefined); + CHECK(sampler.SetState(Rhi::ResourceState::ShaderResource)); + CHECK(sampler.GetState() == Rhi::ResourceState::ShaderResource); + } + + SECTION("Set State with Barriers") + { + Rhi::ResourceBarriers resource_barriers; + CHECK(sampler.SetState(Rhi::ResourceState::CopyDest)); + CHECK(sampler.SetState(Rhi::ResourceState::ShaderResource, resource_barriers)); + CHECK(sampler.GetState() == Rhi::ResourceState::ShaderResource); + CHECK(resource_barriers.HasStateTransition(sampler.GetInterface(), + Rhi::ResourceState::CopyDest, + Rhi::ResourceState::ShaderResource)); + } + + SECTION("Set Owner Queue Family") + { + CHECK_FALSE(sampler.GetOwnerQueueFamily().has_value()); + CHECK(sampler.SetOwnerQueueFamily(1U)); + REQUIRE(sampler.GetOwnerQueueFamily().has_value()); + CHECK(sampler.GetOwnerQueueFamily().value() == 1U); + } + + SECTION("Set Owner Queue Family with Barriers") + { + Rhi::ResourceBarriers resource_barriers; + CHECK(sampler.SetOwnerQueueFamily(0U)); + CHECK(sampler.SetOwnerQueueFamily(1U, resource_barriers)); + REQUIRE(sampler.GetOwnerQueueFamily().has_value()); + CHECK(sampler.GetOwnerQueueFamily().value() == 1U); + CHECK(resource_barriers.HasOwnerTransition(sampler.GetInterface(), 0U, 1U)); + } + + SECTION("Restore Descriptor Views") + { + auto sampler_ptr = std::make_unique(compute_context, sampler_settings); + const Rhi::Sampler::DescriptorByViewId descriptor_by_view_id = sampler_ptr->GetDescriptorByViewId(); + sampler_ptr = std::make_unique(compute_context, sampler_settings); + CHECK_NOTHROW(sampler_ptr->RestoreDescriptorViews(descriptor_by_view_id)); + } +} diff --git a/Tests/Graphics/RHI/ShaderTest.cpp b/Tests/Graphics/RHI/ShaderTest.cpp new file mode 100644 index 000000000..175db37ed --- /dev/null +++ b/Tests/Graphics/RHI/ShaderTest.cpp @@ -0,0 +1,63 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/ShaderTest.cpp +Unit-tests of the RHI Shader + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Shader Functions", "[rhi][shader]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + const Rhi::ShaderSettings shader_settings{ + Data::ShaderProvider::Get(), + Rhi::ShaderEntryFunction{ "Shader", "Main" }, + Rhi::ShaderMacroDefinitions{ { "MACRO_FOO", "1" }, { "MACRO_BAR", "2" } } + }; + + SECTION("Compute Shader Construction from Compute Context") + { + Rhi::Shader compute_shader; + REQUIRE_NOTHROW(compute_shader = compute_context.CreateShader(Rhi::ShaderType::Compute, shader_settings)); + REQUIRE(compute_shader.IsInitialized()); + CHECK(compute_shader.GetInterfacePtr()); + CHECK(compute_shader.GetType() == Rhi::ShaderType::Compute); + CHECK(compute_shader.GetSettings() == shader_settings); + } + + SECTION("Macro-definitions to string") + { + CHECK(Rhi::ShaderMacroDefinition::ToString(shader_settings.compile_definitions, "; ") == + "MACRO_FOO=1; MACRO_BAR=2"); + } +} diff --git a/Tests/Graphics/RHI/TextureTest.cpp b/Tests/Graphics/RHI/TextureTest.cpp new file mode 100644 index 000000000..e4d4e49d6 --- /dev/null +++ b/Tests/Graphics/RHI/TextureTest.cpp @@ -0,0 +1,177 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/TextureTest.cpp +Unit-tests of the RHI Texture + +******************************************************************************/ + +#include "Methane/Graphics/RHI/ResourceView.h" +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Texture Functions", "[rhi][texture][resource]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + const Rhi::TextureSettings image_texture_settings = Rhi::TextureSettings::ForImage(Dimensions(640, 480), {}, PixelFormat::RGBA8, false); + + SECTION("Constant Texture Construction") + { + Rhi::Texture texture; + REQUIRE_NOTHROW(texture = compute_context.CreateTexture(image_texture_settings)); + REQUIRE(texture.IsInitialized()); + CHECK(texture.GetInterfacePtr()); + CHECK(texture.GetResourceType() == Rhi::ResourceType::Texture); + CHECK(texture.GetSettings() == image_texture_settings); + CHECK(texture.GetUsage() == image_texture_settings.usage_mask); + CHECK(std::addressof(texture.GetContext()) == compute_context.GetInterfacePtr().get()); + } + + SECTION("Object Destroyed Callback") + { + auto texture_ptr = std::make_unique(compute_context, image_texture_settings); + ObjectCallbackTester object_callback_tester(*texture_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + texture_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + SECTION("Resource Released Callback") + { + auto texture_ptr = std::make_unique(compute_context, image_texture_settings); + ResourceCallbackTester resource_callback_tester(*texture_ptr); + CHECK_FALSE(resource_callback_tester.IsResourceReleased()); + texture_ptr.reset(); + CHECK(resource_callback_tester.IsResourceReleased()); + } + + const Rhi::Texture texture = compute_context.CreateTexture(image_texture_settings); + + SECTION("Object Name Setup") + { + CHECK(texture.SetName("My Texture")); + CHECK(texture.GetName() == "My Texture"); + } + + SECTION("Object Name Change Callback") + { + CHECK(texture.SetName("My Texture")); + ObjectCallbackTester object_callback_tester(texture); + CHECK(texture.SetName("Our Texture")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Texture"); + CHECK(object_callback_tester.GetOldObjectName() == "My Texture"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(texture.SetName("My Texture")); + ObjectCallbackTester object_callback_tester(texture); + CHECK_FALSE(texture.SetName("My Texture")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Set State") + { + CHECK(texture.GetState() == Rhi::ResourceState::Undefined); + CHECK(texture.SetState(Rhi::ResourceState::ShaderResource)); + CHECK(texture.GetState() == Rhi::ResourceState::ShaderResource); + } + + SECTION("Set State with Barriers") + { + Rhi::ResourceBarriers resource_barriers; + CHECK(texture.SetState(Rhi::ResourceState::CopyDest)); + CHECK(texture.SetState(Rhi::ResourceState::ShaderResource, resource_barriers)); + CHECK(texture.GetState() == Rhi::ResourceState::ShaderResource); + CHECK(resource_barriers.HasStateTransition(texture.GetInterface(), + Rhi::ResourceState::CopyDest, + Rhi::ResourceState::ShaderResource)); + } + + SECTION("Set Owner Queue Family") + { + CHECK_FALSE(texture.GetOwnerQueueFamily().has_value()); + CHECK(texture.SetOwnerQueueFamily(1U)); + REQUIRE(texture.GetOwnerQueueFamily().has_value()); + CHECK(texture.GetOwnerQueueFamily().value() == 1U); + } + + SECTION("Set Owner Queue Family with Barriers") + { + Rhi::ResourceBarriers resource_barriers; + CHECK(texture.SetOwnerQueueFamily(0U)); + CHECK(texture.SetOwnerQueueFamily(1U, resource_barriers)); + REQUIRE(texture.GetOwnerQueueFamily().has_value()); + CHECK(texture.GetOwnerQueueFamily().value() == 1U); + CHECK(resource_barriers.HasOwnerTransition(texture.GetInterface(), 0U, 1U)); + } + + SECTION("Restore Descriptor Views") + { + auto texture_ptr = std::make_unique(compute_context, image_texture_settings); + const Rhi::Texture::DescriptorByViewId descriptor_by_view_id = texture_ptr->GetDescriptorByViewId(); + texture_ptr = std::make_unique(compute_context, image_texture_settings); + CHECK_NOTHROW(texture_ptr->RestoreDescriptorViews(descriptor_by_view_id)); + } + + SECTION("Get Data Size") + { + CHECK(texture.GetDataSize(Data::MemoryState::Reserved) == 1228800U); + CHECK(texture.GetDataSize(Data::MemoryState::Initialized) == 0U); + } + + SECTION("Get SubResource Count and Data Size") + { + CHECK(texture.GetSubresourceCount() == Rhi::SubResourceCount()); + CHECK(texture.GetSubResourceDataSize(Rhi::SubResourceIndex()) == 1228800U); + } + + SECTION("Set Data") + { + std::vector test_data(256, std::byte(8)); + REQUIRE_NOTHROW(texture.SetData(compute_context.GetComputeCommandKit().GetQueue(), { + { + reinterpret_cast(test_data.data()), // NOSONAR + static_cast(test_data.size()) + } + })); + CHECK(texture.GetDataSize(Data::MemoryState::Initialized) == 256U); + } + + SECTION("Get Data") + { + CHECK_NOTHROW(texture.GetData(compute_context.GetComputeCommandKit().GetQueue(), + Rhi::SubResource::Index{}, Rhi::BytesRangeOpt{})); + } +} diff --git a/Tests/Graphics/RHI/TransferCommandListTest.cpp b/Tests/Graphics/RHI/TransferCommandListTest.cpp new file mode 100644 index 000000000..f9ed1bb50 --- /dev/null +++ b/Tests/Graphics/RHI/TransferCommandListTest.cpp @@ -0,0 +1,222 @@ +/****************************************************************************** + +Copyright 2023 Evgeny Gorodetskiy + +Licensed under the Apache License, Version 2.0 (the "License"), +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +******************************************************************************* + +FILE: Tests/Graphics/RHI/TransferCommandListTest.cpp +Unit-tests of the RHI Transfer Command List + +******************************************************************************/ + +#include "RhiTestHelpers.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace Methane; +using namespace Methane::Graphics; + +static tf::Executor g_parallel_executor; + +TEST_CASE("RHI Transfer Command List Functions", "[rhi][list][transfer]") +{ + const Rhi::ComputeContext compute_context = Rhi::ComputeContext(GetTestDevice(), g_parallel_executor, {}); + const Rhi::CommandQueue compute_cmd_queue = compute_context.CreateCommandQueue(Rhi::CommandListType::Compute); + + SECTION("Transfer Command List Construction") + { + Rhi::TransferCommandList cmd_list; + REQUIRE_NOTHROW(cmd_list = compute_cmd_queue.CreateTransferCommandList()); + REQUIRE(cmd_list.IsInitialized()); + CHECK(cmd_list.GetInterfacePtr()); + CHECK(cmd_list.GetCommandQueue().GetInterfacePtr().get() == compute_cmd_queue.GetInterfacePtr().get()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Pending); + } + + SECTION("Object Destroyed Callback") + { + auto cmd_list_ptr = std::make_unique(compute_cmd_queue); + ObjectCallbackTester object_callback_tester(*cmd_list_ptr); + CHECK_FALSE(object_callback_tester.IsObjectDestroyed()); + cmd_list_ptr.reset(); + CHECK(object_callback_tester.IsObjectDestroyed()); + } + + const Rhi::TransferCommandList cmd_list = compute_cmd_queue.CreateTransferCommandList(); + + SECTION("Object Name Setup") + { + CHECK(cmd_list.SetName("My Command List")); + CHECK(cmd_list.GetName() == "My Command List"); + } + + SECTION("Object Name Change Callback") + { + CHECK(cmd_list.SetName("My Command List")); + ObjectCallbackTester object_callback_tester(cmd_list); + CHECK(cmd_list.SetName("Our Command List")); + CHECK(object_callback_tester.IsObjectNameChanged()); + CHECK(object_callback_tester.GetCurObjectName() == "Our Command List"); + CHECK(object_callback_tester.GetOldObjectName() == "My Command List"); + } + + SECTION("Object Name Set Unchanged") + { + CHECK(cmd_list.SetName("My Fence")); + ObjectCallbackTester object_callback_tester(cmd_list); + CHECK_FALSE(cmd_list.SetName("My Fence")); + CHECK_FALSE(object_callback_tester.IsObjectNameChanged()); + } + + SECTION("Reset Command List") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + } + + SECTION("Reset Command List Once") + { + REQUIRE_NOTHROW(cmd_list.ResetOnce()); + REQUIRE_NOTHROW(cmd_list.ResetOnce()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + } + + SECTION("Reset Command List with Debug Group") + { + const Rhi::CommandListDebugGroup debug_group("Test"); + REQUIRE_NOTHROW(cmd_list.Reset(&debug_group)); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK(dynamic_cast(cmd_list.GetInterface()).GetTopOpenDebugGroup()->GetName() == "Test"); + } + + SECTION("Reset Command List Once with Debug Group") + { + const Rhi::CommandListDebugGroup debug_group("Test"); + REQUIRE_NOTHROW(cmd_list.ResetOnce(&debug_group)); + REQUIRE_NOTHROW(cmd_list.ResetOnce(&debug_group)); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK(dynamic_cast(cmd_list.GetInterface()).GetTopOpenDebugGroup()->GetName() == "Test"); + } + + SECTION("Push and Pop Debug Group") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK_NOTHROW(cmd_list.PushDebugGroup(Rhi::CommandListDebugGroup("Test"))); + CHECK_NOTHROW(cmd_list.PopDebugGroup()); + } + + SECTION("Can not Pop Missing Debug Group") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK_THROWS(cmd_list.PopDebugGroup()); + } + + SECTION("Set Resource Barriers") + { + const Rhi::ResourceBarriers barriers(Rhi::IResourceBarriers::Set{}); + REQUIRE_NOTHROW(cmd_list.Reset()); + REQUIRE_NOTHROW(cmd_list.SetResourceBarriers(barriers.GetInterface())); + } + + SECTION("Commit Command List") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK_NOTHROW(cmd_list.Commit()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Committed); + } + + SECTION("Execute Command List with Callback Tracker") + { + CommandListCallbackTester cmd_list_callback_tester(cmd_list); + const Rhi::CommandListSet cmd_list_set({ cmd_list.GetInterface() }); + + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Encoding); + CHECK(cmd_list_callback_tester.GetTrackingState() == Rhi::CommandListState::Encoding); + CHECK(cmd_list_callback_tester.IsStateChanged()); + CHECK_FALSE(cmd_list_callback_tester.IsExecutionCompleted()); + + cmd_list_callback_tester.Reset(); + + REQUIRE_NOTHROW(cmd_list.Commit()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Committed); + CHECK(cmd_list_callback_tester.GetTrackingState() == Rhi::CommandListState::Committed); + CHECK(cmd_list_callback_tester.IsStateChanged()); + CHECK_FALSE(cmd_list_callback_tester.IsExecutionCompleted()); + + cmd_list_callback_tester.Reset(); + + Rhi::ICommandList* completed_command_list_ptr = nullptr; + REQUIRE_NOTHROW(compute_cmd_queue.Execute(cmd_list_set, + [&completed_command_list_ptr](Rhi::ICommandList& command_list) { + completed_command_list_ptr = &command_list; + })); + + CHECK(cmd_list.GetState() == Rhi::CommandListState::Executing); + CHECK(cmd_list_callback_tester.GetTrackingState() == Rhi::CommandListState::Executing); + CHECK(cmd_list_callback_tester.IsStateChanged()); + CHECK_FALSE(cmd_list_callback_tester.IsExecutionCompleted()); + CHECK_FALSE(completed_command_list_ptr); + + cmd_list_callback_tester.Reset(); + dynamic_cast(cmd_list_set.GetInterface()).Complete(); + + CHECK(cmd_list.GetState() == Rhi::CommandListState::Pending); + CHECK(cmd_list_callback_tester.GetTrackingState() == Rhi::CommandListState::Pending); + CHECK(cmd_list_callback_tester.IsExecutionCompleted()); + CHECK(completed_command_list_ptr == cmd_list.GetInterfacePtr().get()); + } + + SECTION("Wait Until Command List Completed") + { + const Rhi::CommandListSet cmd_list_set({ cmd_list.GetInterface() }); + REQUIRE_NOTHROW(cmd_list.Reset()); + REQUIRE_NOTHROW(cmd_list.Commit()); + REQUIRE_NOTHROW(compute_cmd_queue.Execute(cmd_list_set)); + + auto async_complete = std::async(std::launch::async, [&cmd_list_set]() + { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + dynamic_cast(cmd_list_set.GetInterface()).Complete(); + }); + + CHECK(cmd_list.GetState() == Rhi::CommandListState::Executing); + REQUIRE_NOTHROW(cmd_list.WaitUntilCompleted()); + CHECK(cmd_list.GetState() == Rhi::CommandListState::Pending); + } + + SECTION("Get GPU Time Range") + { + REQUIRE_NOTHROW(cmd_list.Reset()); + CHECK_NOTHROW(cmd_list.GetGpuTimeRange(true) == Data::TimeRange{}); + CHECK_NOTHROW(cmd_list.GetGpuTimeRange(false) == Data::TimeRange{}); + } +} diff --git a/Tests/UserInterface/Types/CMakeLists.txt b/Tests/UserInterface/Types/CMakeLists.txt index 794abcd50..53dc3fa76 100644 --- a/Tests/UserInterface/Types/CMakeLists.txt +++ b/Tests/UserInterface/Types/CMakeLists.txt @@ -14,6 +14,7 @@ target_link_libraries(${TARGET} MethaneUserInterfaceNullTypes MethaneUserInterfaceNullTypography MethanePlatformApp + TaskFlow $<$:TracyClient> Catch2WithMain ) diff --git a/Tests/UserInterface/Types/ContextTest.cpp b/Tests/UserInterface/Types/ContextTest.cpp index f30d9aada..e19432be1 100644 --- a/Tests/UserInterface/Types/ContextTest.cpp +++ b/Tests/UserInterface/Types/ContextTest.cpp @@ -33,6 +33,7 @@ Unit-tests of the User Interface IContext #include #include +#include #include #include @@ -41,17 +42,12 @@ using namespace Methane::Graphics; using namespace Methane::Platform; using namespace Methane::UserInterface; -namespace tf -{ -class Executor { public: Executor() = default; }; -} - static const float g_dot_to_px_factor = 2.F; static const uint32_t g_font_resolution_dpi = 96; static const FakeApp g_fake_app(2.F, 96); static const UnitSize g_frame_size_dot { Units::Dots, 960U, 540U }; static const UnitSize g_frame_size_px { Units::Pixels, 1920U, 1080U }; -static tf::Executor g_fake_executor; +static tf::Executor g_parallel_executor; static Rhi::Device GetTestDevice() { @@ -62,7 +58,7 @@ static Rhi::Device GetTestDevice() TEST_CASE("UI Context Accessors", "[ui][context][accessor]") { - const Rhi::RenderContext render_context(AppEnvironment{}, GetTestDevice(), g_fake_executor, Rhi::RenderContextSettings{ g_frame_size_px.AsBase() }); + const Rhi::RenderContext render_context(AppEnvironment{}, GetTestDevice(), g_parallel_executor, Rhi::RenderContextSettings{ g_frame_size_px.AsBase() }); const Rhi::CommandQueue render_cmd_queue(render_context, Rhi::CommandListType::Render); const Rhi::RenderPattern render_pattern(render_context, Rhi::RenderPatternSettings{}); UserInterface::Context ui_context(g_fake_app, render_cmd_queue, render_pattern); @@ -92,7 +88,7 @@ TEST_CASE("UI Context Accessors", "[ui][context][accessor]") TEMPLATE_TEST_CASE("UI Context Convertors of Unit Types", "[ui][context][unit][convert]", ALL_BASE_TYPES) { -const Rhi::RenderContext render_context(AppEnvironment{}, GetTestDevice(), g_fake_executor, Rhi::RenderContextSettings{ g_frame_size_px.AsBase() }); +const Rhi::RenderContext render_context(AppEnvironment{}, GetTestDevice(), g_parallel_executor, Rhi::RenderContextSettings{ g_frame_size_px.AsBase() }); const Rhi::CommandQueue render_cmd_queue(render_context, Rhi::CommandListType::Render); const Rhi::RenderPattern render_pattern(render_context, Rhi::RenderPatternSettings{}); UserInterface::Context ui_context(g_fake_app, render_cmd_queue, render_pattern); @@ -184,7 +180,7 @@ UserInterface::Context ui_context(g_fake_app, render_cmd_queue, render_pattern); TEMPLATE_TEST_CASE("UI Context Comparison of Unit Types", "[ui][context][unit][convert]", ALL_BASE_TYPES) { - const Rhi::RenderContext render_context(AppEnvironment{}, GetTestDevice(), g_fake_executor, Rhi::RenderContextSettings{ g_frame_size_px.AsBase() }); + const Rhi::RenderContext render_context(AppEnvironment{}, GetTestDevice(), g_parallel_executor, Rhi::RenderContextSettings{ g_frame_size_px.AsBase() }); const Rhi::CommandQueue render_cmd_queue(render_context, Rhi::CommandListType::Render); const Rhi::RenderPattern render_pattern(render_context, Rhi::RenderPatternSettings{}); UserInterface::Context ui_context(g_fake_app, render_cmd_queue, render_pattern); @@ -210,7 +206,7 @@ TEMPLATE_TEST_CASE("UI Context Comparison of Unit Types", "[ui][context][unit][c TEMPLATE_TEST_CASE("UI Context Convertors of Scalar Types", "[ui][context][unit][convert]", int32_t, uint32_t, float, double) { - const Rhi::RenderContext render_context(AppEnvironment{}, GetTestDevice(), g_fake_executor, Rhi::RenderContextSettings{ g_frame_size_px.AsBase() }); + const Rhi::RenderContext render_context(AppEnvironment{}, GetTestDevice(), g_parallel_executor, Rhi::RenderContextSettings{ g_frame_size_px.AsBase() }); const Rhi::CommandQueue render_cmd_queue(render_context, Rhi::CommandListType::Render); const Rhi::RenderPattern render_pattern(render_context, Rhi::RenderPatternSettings{}); UserInterface::Context ui_context(g_fake_app, render_cmd_queue, render_pattern);