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 94%
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