Compare commits
	
		
			14 commits
		
	
	
		
			feat/test_
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4534ed11d2 | |||
| d029c0e473 | |||
| 604ee5e6a1 | |||
| 7ee4381bbf | |||
| 8730d31e2f | |||
| f50208653e | |||
| 5422792705 | |||
| 2ddb90faff | |||
| 736c37d2f1 | |||
| 97ca429d38 | |||
| 5a404d5269 | |||
| a9e27d6935 | |||
| 80662983a3 | |||
| c39ce89a9b | 
					 55 changed files with 1241 additions and 876 deletions
				
			
		
							
								
								
									
										5
									
								
								.clangd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.clangd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | CompileFlags: | ||||||
|  |   DriverMode: cl | ||||||
|  |   Add: | ||||||
|  |     - /EHsc | ||||||
|  |     - /std:c++latest | ||||||
							
								
								
									
										290
									
								
								.drone.yml
									
										
									
									
									
								
							
							
						
						
									
										290
									
								
								.drone.yml
									
										
									
									
									
								
							|  | @ -1,42 +1,42 @@ | ||||||
| # --- | --- | ||||||
| # kind: pipeline | kind: pipeline | ||||||
| # type: exec | type: exec | ||||||
| # name: amd64 — msvc | name: amd64 — msvc | ||||||
| # trigger: | trigger: | ||||||
| #   branch: |   branch: | ||||||
| #   - main |   - main | ||||||
| # platform: | platform: | ||||||
| #   os: windows |   os: windows | ||||||
| #   arch: amd64 |   arch: amd64 | ||||||
| # | 
 | ||||||
| # steps: | steps: | ||||||
| # - name: unit tests | - name: unit tests | ||||||
| #   shell: powershell |   shell: powershell | ||||||
| #   commands:  |   commands:  | ||||||
| #     - ./tools/ci/amd64/msvc/unit_tests.ps1 |     - ./tools/ci/amd64/msvc/unit_tests.ps1 | ||||||
| # | 
 | ||||||
| # --- | --- | ||||||
| # kind: pipeline | kind: pipeline | ||||||
| # type: docker | type: docker | ||||||
| # name: amd64 — gcc | name: amd64 — gcc | ||||||
| # trigger: | trigger: | ||||||
| #   branch: |   branch: | ||||||
| #   - main |   - main | ||||||
| # | 
 | ||||||
| # steps: | steps: | ||||||
| # - name: unit tests | - name: unit tests | ||||||
| #   image: ci:latest |   image: ci:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   commands: |   commands: | ||||||
| #     - ./tools/ci/amd64/gcc/unit_tests.sh |     - ./tools/ci/amd64/gcc/unit_tests.sh | ||||||
| # | 
 | ||||||
| # - name: valgrind | - name: valgrind | ||||||
| #   image: ci:latest |   image: ci:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   commands: |   commands: | ||||||
| #     - ./tools/ci/amd64/gcc/valgrind.sh |     - ./tools/ci/amd64/gcc/valgrind.sh | ||||||
| # | 
 | ||||||
| # --- | --- | ||||||
| kind: pipeline | kind: pipeline | ||||||
| type: docker | type: docker | ||||||
| name: amd64 — clang | name: amd64 — clang | ||||||
|  | @ -45,114 +45,114 @@ trigger: | ||||||
|   - main |   - main | ||||||
| 
 | 
 | ||||||
| steps: | steps: | ||||||
| # - name: code coverage | - name: code coverage | ||||||
| #   image: ci:latest |   image: ci:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   environment: |   environment: | ||||||
| #     CODECOV_TOKEN: |     CODECOV_TOKEN: | ||||||
| #       from_secret: CODECOV_TOKEN  |       from_secret: CODECOV_TOKEN  | ||||||
| #   commands: |   commands: | ||||||
| #     - ./tools/ci/amd64/clang/coverage.sh |     - ./tools/ci/amd64/clang/coverage.sh | ||||||
| # | 
 | ||||||
| # - name: leak sanitizer | - name: leak sanitizer | ||||||
| #   image: ci:latest |   image: ci:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   commands: |   commands: | ||||||
| #     - ./tools/ci/amd64/clang/lsan.sh |     - ./tools/ci/amd64/clang/lsan.sh | ||||||
| # | 
 | ||||||
| - name: memory sanitizer | - name: memory sanitizer | ||||||
|   image: ci:latest |   image: ci:latest | ||||||
|   pull: if-not-exists |   pull: if-not-exists | ||||||
|   commands: |   commands: | ||||||
|     - ./tools/ci/amd64/clang/msan.sh |     - ./tools/ci/amd64/clang/msan.sh | ||||||
| # | 
 | ||||||
| # --- | --- | ||||||
| # kind: pipeline | kind: pipeline | ||||||
| # type: docker | type: docker | ||||||
| # name: static analysis | name: static analysis | ||||||
| # trigger: | trigger: | ||||||
| #   branch: |   branch: | ||||||
| #   - main |   - main | ||||||
| # | 
 | ||||||
| # steps: | steps: | ||||||
| # - name: clang tidy | - name: clang tidy | ||||||
| #   image: ci:latest |   image: ci:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   privileged: true |   privileged: true | ||||||
| #   commands: |   commands: | ||||||
| #     - ./tools/ci/static_analysis/clang_tidy.sh |     - ./tools/ci/static_analysis/clang_tidy.sh | ||||||
| # | 
 | ||||||
| # - name: shell check | - name: shell check | ||||||
| #   image: ci:latest |   image: ci:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   commands: |   commands: | ||||||
| #     - ./tools/ci/static_analysis/shell_check.sh |     - ./tools/ci/static_analysis/shell_check.sh | ||||||
| # | 
 | ||||||
| # - name: clang format | - name: clang format | ||||||
| #   image: ci:latest |   image: ci:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   commands: |   commands: | ||||||
| #     - ./tools/ci/static_analysis/clang_format.sh |     - ./tools/ci/static_analysis/clang_format.sh | ||||||
| # | 
 | ||||||
| # - name: cmake format | - name: cmake format | ||||||
| #   image: ci:latest |   image: ci:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   commands: |   commands: | ||||||
| #     - ./tools/ci/static_analysis/cmake_format.sh |     - ./tools/ci/static_analysis/cmake_format.sh | ||||||
| # | 
 | ||||||
| # - name: shell format | - name: shell format | ||||||
| #   image: ci:latest |   image: ci:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   commands: |   commands: | ||||||
| #     - ./tools/ci/static_analysis/shell_format.sh |     - ./tools/ci/static_analysis/shell_format.sh | ||||||
| # | 
 | ||||||
| # --- | --- | ||||||
| # kind: pipeline | kind: pipeline | ||||||
| # type: docker  | type: docker  | ||||||
| # name: documentation — development | name: documentation — development | ||||||
| # node: | node: | ||||||
| #   environment: ryali |   environment: ryali | ||||||
| # trigger: | trigger: | ||||||
| #   branch: |   branch: | ||||||
| #   - main |   - main | ||||||
| # | 
 | ||||||
| # steps: | steps: | ||||||
| # - name: build and deploy | - name: build and deploy | ||||||
| #   image: documentation:latest |   image: documentation:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   commands: |   commands: | ||||||
| #     - pwd |     - pwd | ||||||
| #     - cd docs |     - cd docs | ||||||
| #     - mkdir generated |     - mkdir generated | ||||||
| #     - touch generated/changelogs.rst |     - touch generated/changelogs.rst | ||||||
| #     - touch generated/api.rst |     - touch generated/api.rst | ||||||
| #     - sphinx-build -M html . . |     - sphinx-build -M html . . | ||||||
| # | 
 | ||||||
| #     - rm -rf /light_docs_dev/* |     - rm -rf /light_docs_dev/* | ||||||
| #     - mv ./html/* /light_docs_dev/ |     - mv ./html/* /light_docs_dev/ | ||||||
| # | 
 | ||||||
| # --- | --- | ||||||
| # | 
 | ||||||
| # kind: pipeline | kind: pipeline | ||||||
| # type: docker | type: docker | ||||||
| # name: documentation — production | name: documentation — production | ||||||
| # node: | node: | ||||||
| #   environment: ryali |   environment: ryali | ||||||
| # trigger: | trigger: | ||||||
| #   event: |   event: | ||||||
| #   - tag |   - tag | ||||||
| # | 
 | ||||||
| # steps: | steps: | ||||||
| # - name: build and deploy | - name: build and deploy | ||||||
| #   image: documentation:latest |   image: documentation:latest | ||||||
| #   pull: if-not-exists |   pull: if-not-exists | ||||||
| #   commands: |   commands: | ||||||
| #     - cd docs |     - cd docs | ||||||
| #     - mkdir generated |     - mkdir generated | ||||||
| #     - touch generated/changelogs.rst |     - touch generated/changelogs.rst | ||||||
| #     - touch generated/api.rst |     - touch generated/api.rst | ||||||
| #     - sphinx-build -M html . . |     - sphinx-build -M html . . | ||||||
| # | 
 | ||||||
| #     - rm -rf /light_docs/* |     - rm -rf /light_docs/* | ||||||
| #     - mv ./html/* /light_docs/ |     - mv ./html/* /light_docs/ | ||||||
| # | 
 | ||||||
|  |  | ||||||
|  | @ -1,128 +0,0 @@ | ||||||
| # Contributor Covenant Code of Conduct |  | ||||||
| 
 |  | ||||||
| ## Our Pledge |  | ||||||
| 
 |  | ||||||
| We as members, contributors, and leaders pledge to make participation in our |  | ||||||
| community a harassment-free experience for everyone, regardless of age, body |  | ||||||
| size, visible or invisible disability, ethnicity, sex characteristics, gender |  | ||||||
| identity and expression, level of experience, education, socio-economic status, |  | ||||||
| nationality, personal appearance, race, religion, or sexual identity |  | ||||||
| and orientation. |  | ||||||
| 
 |  | ||||||
| We pledge to act and interact in ways that contribute to an open, welcoming, |  | ||||||
| diverse, inclusive, and healthy community. |  | ||||||
| 
 |  | ||||||
| ## Our Standards |  | ||||||
| 
 |  | ||||||
| Examples of behavior that contributes to a positive environment for our |  | ||||||
| community include: |  | ||||||
| 
 |  | ||||||
| * Demonstrating empathy and kindness toward other people |  | ||||||
| * Being respectful of differing opinions, viewpoints, and experiences |  | ||||||
| * Giving and gracefully accepting constructive feedback |  | ||||||
| * Accepting responsibility and apologizing to those affected by our mistakes, |  | ||||||
|   and learning from the experience |  | ||||||
| * Focusing on what is best not just for us as individuals, but for the |  | ||||||
|   overall community |  | ||||||
| 
 |  | ||||||
| Examples of unacceptable behavior include: |  | ||||||
| 
 |  | ||||||
| * The use of sexualized language or imagery, and sexual attention or |  | ||||||
|   advances of any kind |  | ||||||
| * Trolling, insulting or derogatory comments, and personal or political attacks |  | ||||||
| * Public or private harassment |  | ||||||
| * Publishing others' private information, such as a physical or email |  | ||||||
|   address, without their explicit permission |  | ||||||
| * Other conduct which could reasonably be considered inappropriate in a |  | ||||||
|   professional setting |  | ||||||
| 
 |  | ||||||
| ## Enforcement Responsibilities |  | ||||||
| 
 |  | ||||||
| Community leaders are responsible for clarifying and enforcing our standards of |  | ||||||
| acceptable behavior and will take appropriate and fair corrective action in |  | ||||||
| response to any behavior that they deem inappropriate, threatening, offensive, |  | ||||||
| or harmful. |  | ||||||
| 
 |  | ||||||
| Community leaders have the right and responsibility to remove, edit, or reject |  | ||||||
| comments, commits, code, wiki edits, issues, and other contributions that are |  | ||||||
| not aligned to this Code of Conduct, and will communicate reasons for moderation |  | ||||||
| decisions when appropriate. |  | ||||||
| 
 |  | ||||||
| ## Scope |  | ||||||
| 
 |  | ||||||
| This Code of Conduct applies within all community spaces, and also applies when |  | ||||||
| an individual is officially representing the community in public spaces. |  | ||||||
| Examples of representing our community include using an official e-mail address, |  | ||||||
| posting via an official social media account, or acting as an appointed |  | ||||||
| representative at an online or offline event. |  | ||||||
| 
 |  | ||||||
| ## Enforcement |  | ||||||
| 
 |  | ||||||
| Instances of abusive, harassing, or otherwise unacceptable behavior may be |  | ||||||
| reported to the community leaders responsible for enforcement at |  | ||||||
| Discord: Light7734#4652. |  | ||||||
| All complaints will be reviewed and investigated promptly and fairly. |  | ||||||
| 
 |  | ||||||
| All community leaders are obligated to respect the privacy and security of the |  | ||||||
| reporter of any incident. |  | ||||||
| 
 |  | ||||||
| ## Enforcement Guidelines |  | ||||||
| 
 |  | ||||||
| Community leaders will follow these Community Impact Guidelines in determining |  | ||||||
| the consequences for any action they deem in violation of this Code of Conduct: |  | ||||||
| 
 |  | ||||||
| ### 1. Correction |  | ||||||
| 
 |  | ||||||
| **Community Impact**: Use of inappropriate language or other behavior deemed |  | ||||||
| unprofessional or unwelcome in the community. |  | ||||||
| 
 |  | ||||||
| **Consequence**: A private, written warning from community leaders, providing |  | ||||||
| clarity around the nature of the violation and an explanation of why the |  | ||||||
| behavior was inappropriate. A public apology may be requested. |  | ||||||
| 
 |  | ||||||
| ### 2. Warning |  | ||||||
| 
 |  | ||||||
| **Community Impact**: A violation through a single incident or series |  | ||||||
| of actions. |  | ||||||
| 
 |  | ||||||
| **Consequence**: A warning with consequences for continued behavior. No |  | ||||||
| interaction with the people involved, including unsolicited interaction with |  | ||||||
| those enforcing the Code of Conduct, for a specified period of time. This |  | ||||||
| includes avoiding interactions in community spaces as well as external channels |  | ||||||
| like social media. Violating these terms may lead to a temporary or |  | ||||||
| permanent ban. |  | ||||||
| 
 |  | ||||||
| ### 3. Temporary Ban |  | ||||||
| 
 |  | ||||||
| **Community Impact**: A serious violation of community standards, including |  | ||||||
| sustained inappropriate behavior. |  | ||||||
| 
 |  | ||||||
| **Consequence**: A temporary ban from any sort of interaction or public |  | ||||||
| communication with the community for a specified period of time. No public or |  | ||||||
| private interaction with the people involved, including unsolicited interaction |  | ||||||
| with those enforcing the Code of Conduct, is allowed during this period. |  | ||||||
| Violating these terms may lead to a permanent ban. |  | ||||||
| 
 |  | ||||||
| ### 4. Permanent Ban |  | ||||||
| 
 |  | ||||||
| **Community Impact**: Demonstrating a pattern of violation of community |  | ||||||
| standards, including sustained inappropriate behavior,  harassment of an |  | ||||||
| individual, or aggression toward or disparagement of classes of individuals. |  | ||||||
| 
 |  | ||||||
| **Consequence**: A permanent ban from any sort of public interaction within |  | ||||||
| the community. |  | ||||||
| 
 |  | ||||||
| ## Attribution |  | ||||||
| 
 |  | ||||||
| This Code of Conduct is adapted from the [Contributor Covenant][homepage], |  | ||||||
| version 2.0, available at |  | ||||||
| https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. |  | ||||||
| 
 |  | ||||||
| Community Impact Guidelines were inspired by [Mozilla's code of conduct |  | ||||||
| enforcement ladder](https://github.com/mozilla/diversity). |  | ||||||
| 
 |  | ||||||
| [homepage]: https://www.contributor-covenant.org |  | ||||||
| 
 |  | ||||||
| For answers to common questions about this code of conduct, see the FAQ at |  | ||||||
| https://www.contributor-covenant.org/faq. Translations are available at |  | ||||||
| https://www.contributor-covenant.org/translations. |  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -1,21 +1,26 @@ | ||||||
| #version 450 core | #version 450 core | ||||||
| 
 | 
 | ||||||
| vec2 positions[3] = vec2[]( | layout(push_constant ) uniform pc { | ||||||
|     vec2(0.0, -0.5), |  mat4 view_projection; | ||||||
|     vec2(0.5, 0.5), | }; | ||||||
|     vec2(-0.5, 0.5) | 
 | ||||||
|  | vec3 positions[3] = vec3[]( | ||||||
|  |     vec3(0.0, -0.5, 0.5), | ||||||
|  |     vec3(0.5, 0.5, 0.5), | ||||||
|  |     vec3(-0.5, 0.5, 0.5) | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| vec3 colors[3] = vec3[]( | vec3 colors[3] = vec3[]( | ||||||
|     vec3(1.0, 0.0, 0.0), |     vec3(0.0, 0.0, 0.0), | ||||||
|     vec3(0.0, 1.0, 0.0), |     vec3(0.0, 0.0, 0.0), | ||||||
|     vec3(0.0, 0.0, 1.0) |     vec3(0.0, 0.0, 0.0) | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| layout(location = 0) out vec3 out_frag_color; | layout(location = 0) out vec3 out_frag_color; | ||||||
| 
 | 
 | ||||||
| void main()  | void main()  | ||||||
| { | { | ||||||
|     gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); |     gl_Position = view_projection * vec4(positions[gl_VertexIndex], 1.0); | ||||||
|     out_frag_color = colors[gl_VertexIndex]; |     out_frag_color = colors[gl_VertexIndex]; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -1,3 +1,3 @@ | ||||||
| add_library_module(camera camera.cpp scene.cpp) | add_library_module(camera) | ||||||
| 
 | 
 | ||||||
| target_link_libraries(camera PUBLIC math) | target_link_libraries(camera INTERFACE math) | ||||||
|  |  | ||||||
|  | @ -1,6 +0,0 @@ | ||||||
| #include <camera/camera.hpp> |  | ||||||
| 
 |  | ||||||
| namespace lt { |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,84 +0,0 @@ | ||||||
| #include <camera/camera.hpp> |  | ||||||
| #include <camera/component.hpp> |  | ||||||
| #include <math/algebra.hpp> |  | ||||||
| #include <math/trig.hpp> |  | ||||||
| 
 |  | ||||||
| namespace lt { |  | ||||||
| 
 |  | ||||||
| SceneCamera::SceneCamera() |  | ||||||
|     : m_orthographic_specification { .size = 1000.0f, .near_plane = -1.0f, .far_plane = 10000.0f } |  | ||||||
|     , m_perspective_specification { .vertical_fov = math::radians(45.0f), |  | ||||||
| 	                                .near_plane = 0.01f, |  | ||||||
| 	                                .far_plane = 10000.0f } |  | ||||||
|     , m_aspect_ratio(16.0f / 9.0f) |  | ||||||
| 
 |  | ||||||
| { |  | ||||||
| 	calculate_projection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCamera::set_viewport_size(unsigned int width, unsigned int height) |  | ||||||
| { |  | ||||||
| 	m_aspect_ratio = static_cast<float>(width) / static_cast<float>(height); |  | ||||||
| 	calculate_projection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCamera::set_projection_type(ProjectionType projection_type) |  | ||||||
| { |  | ||||||
| 	m_projection_type = projection_type; |  | ||||||
| 	calculate_projection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCamera::set_orthographic_size(float size) |  | ||||||
| { |  | ||||||
| 	m_orthographic_specification.size = size; |  | ||||||
| 	calculate_projection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCamera::set_orthographic_far_plane(float far_plane) |  | ||||||
| { |  | ||||||
| 	m_orthographic_specification.far_plane = far_plane; |  | ||||||
| 	calculate_projection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCamera::set_orthographic_near_plane(float near_plane) |  | ||||||
| { |  | ||||||
| 	m_orthographic_specification.near_plane = near_plane; |  | ||||||
| 	calculate_projection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCamera::set_perspective_vertical_fov(float vertical_fov) |  | ||||||
| { |  | ||||||
| 	m_perspective_specification.vertical_fov = vertical_fov; |  | ||||||
| 	calculate_projection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCamera::set_perspective_far_plane(float far_plane) |  | ||||||
| { |  | ||||||
| 	m_perspective_specification.far_plane = far_plane; |  | ||||||
| 	calculate_projection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCamera::set_perspective_near_plane(float near_plane) |  | ||||||
| { |  | ||||||
| 	m_perspective_specification.near_plane = near_plane; |  | ||||||
| 	calculate_projection(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SceneCamera::calculate_projection() |  | ||||||
| { |  | ||||||
| 	// TODO(Light): implement ortho perspective
 |  | ||||||
| 	if (m_projection_type == ProjectionType::Orthographic) |  | ||||||
| 	{ |  | ||||||
| 		// throw std::runtime_error { "ortho perspective not supported yet" };
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// defaults to perspective for now...
 |  | ||||||
| 	m_projection = math::perspective( |  | ||||||
| 	    m_perspective_specification.vertical_fov, |  | ||||||
| 	    m_aspect_ratio, |  | ||||||
| 	    m_perspective_specification.near_plane, |  | ||||||
| 	    m_perspective_specification.far_plane |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace lt
 |  | ||||||
|  | @ -1,35 +0,0 @@ | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <math/mat4.hpp> |  | ||||||
| #include <math/vec4.hpp> |  | ||||||
| 
 |  | ||||||
| namespace lt { |  | ||||||
| 
 |  | ||||||
| class Camera |  | ||||||
| { |  | ||||||
| public: |  | ||||||
| 	Camera() = default; |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_projection() const -> const math::mat4 & |  | ||||||
| 	{ |  | ||||||
| 		return m_projection; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_background_color() const -> const math::vec4 & |  | ||||||
| 	{ |  | ||||||
| 		return m_background_color; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	void set_background_color(const math::vec4 &color) |  | ||||||
| 	{ |  | ||||||
| 		m_background_color = color; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| protected: |  | ||||||
| 	math::mat4 m_projection; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| 	math::vec4 m_background_color = math::vec4(1.0f, 0.0f, 0.0f, 1.0f); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| } // namespace lt
 |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <camera/scene.hpp> |  | ||||||
| 
 |  | ||||||
| namespace lt { |  | ||||||
| 
 |  | ||||||
| struct CameraComponent |  | ||||||
| { |  | ||||||
| 	CameraComponent() = default; |  | ||||||
| 
 |  | ||||||
| 	CameraComponent(const CameraComponent &) = default; |  | ||||||
| 
 |  | ||||||
| 	CameraComponent(SceneCamera _camera, bool _isPrimary = false) |  | ||||||
| 	    : camera(_camera) |  | ||||||
| 	    , isPrimary(_isPrimary) |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	operator SceneCamera() const |  | ||||||
| 	{ |  | ||||||
| 		return camera; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	SceneCamera camera; |  | ||||||
| 
 |  | ||||||
| 	bool isPrimary {}; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| } // namespace lt
 |  | ||||||
							
								
								
									
										22
									
								
								modules/camera/public/components.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								modules/camera/public/components.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <math/mat4.hpp> | ||||||
|  | 
 | ||||||
|  | namespace lt::camera::components { | ||||||
|  | 
 | ||||||
|  | struct PerspectiveCamera | ||||||
|  | { | ||||||
|  | 	float vertical_fov {}; | ||||||
|  | 
 | ||||||
|  | 	float near_plane {}; | ||||||
|  | 
 | ||||||
|  | 	float far_plane {}; | ||||||
|  | 
 | ||||||
|  | 	float aspect_ratio {}; | ||||||
|  | 
 | ||||||
|  | 	math::vec4 background_color; | ||||||
|  | 
 | ||||||
|  | 	bool is_primary {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace lt::camera::components
 | ||||||
|  | @ -1,100 +0,0 @@ | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <camera/camera.hpp> |  | ||||||
| 
 |  | ||||||
| namespace lt { |  | ||||||
| 
 |  | ||||||
| class SceneCamera: public Camera |  | ||||||
| { |  | ||||||
| public: |  | ||||||
| 	enum class ProjectionType |  | ||||||
| 	{ |  | ||||||
| 		Orthographic = 0, |  | ||||||
| 		Perspetcive = 1 |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	struct OrthographicSpecification |  | ||||||
| 	{ |  | ||||||
| 		float size; |  | ||||||
| 
 |  | ||||||
| 		float near_plane; |  | ||||||
| 
 |  | ||||||
| 		float far_plane; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	struct PerspectiveSpecification |  | ||||||
| 	{ |  | ||||||
| 		float vertical_fov; |  | ||||||
| 
 |  | ||||||
| 		float near_plane; |  | ||||||
| 
 |  | ||||||
| 		float far_plane; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	SceneCamera(); |  | ||||||
| 
 |  | ||||||
| 	void set_viewport_size(unsigned int width, unsigned int height); |  | ||||||
| 
 |  | ||||||
| 	void set_projection_type(ProjectionType projection_type); |  | ||||||
| 
 |  | ||||||
| 	void set_orthographic_size(float size); |  | ||||||
| 
 |  | ||||||
| 	void set_orthographic_far_plane(float far_plane); |  | ||||||
| 
 |  | ||||||
| 	void set_orthographic_near_plane(float near_plane); |  | ||||||
| 
 |  | ||||||
| 	void set_perspective_vertical_fov(float vertical_fov); |  | ||||||
| 
 |  | ||||||
| 	void set_perspective_far_plane(float far_plane); |  | ||||||
| 
 |  | ||||||
| 	void set_perspective_near_plane(float near_plane); |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_orthographic_size() const -> float |  | ||||||
| 	{ |  | ||||||
| 		return m_orthographic_specification.size; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_orthographic_far_plane() const -> float |  | ||||||
| 	{ |  | ||||||
| 		return m_orthographic_specification.far_plane; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_orthographic_near_plane() const -> float |  | ||||||
| 	{ |  | ||||||
| 		return m_orthographic_specification.near_plane; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_perspective_vertical_fov() const -> float |  | ||||||
| 	{ |  | ||||||
| 		return m_perspective_specification.vertical_fov; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_perspective_far_plane() const -> float |  | ||||||
| 	{ |  | ||||||
| 		return m_perspective_specification.far_plane; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_perspective_near_plane() const -> float |  | ||||||
| 	{ |  | ||||||
| 		return m_perspective_specification.near_plane; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_projection_type() const -> ProjectionType |  | ||||||
| 	{ |  | ||||||
| 		return m_projection_type; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| 	OrthographicSpecification m_orthographic_specification; |  | ||||||
| 
 |  | ||||||
| 	PerspectiveSpecification m_perspective_specification; |  | ||||||
| 
 |  | ||||||
| 	float m_aspect_ratio; |  | ||||||
| 
 |  | ||||||
| 	ProjectionType m_projection_type { ProjectionType::Orthographic }; |  | ||||||
| 
 |  | ||||||
| 	void calculate_projection(); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| } // namespace lt
 |  | ||||||
|  | @ -31,25 +31,29 @@ namespace lt::math { | ||||||
|  * |  * | ||||||
|  * the 1 at [z][3] is to save the Z axis into the resulting W for perspective division. |  * the 1 at [z][3] is to save the Z axis into the resulting W for perspective division. | ||||||
|  * |  * | ||||||
|  * thanks to pikuma: https://www.youtube.com/watch?v=EqNcqBdrNyI
 |  * @ref Thanks to pikuma for explaining the math behind this: | ||||||
|  |  * https://www.youtube.com/watch?v=EqNcqBdrNyI
 | ||||||
|  */ |  */ | ||||||
| template<typename T> | template<typename T> | ||||||
| constexpr auto perspective(T field_of_view, T aspect_ratio, T z_near, T z_far) | constexpr auto perspective(T field_of_view, T aspect_ratio, T z_near, T z_far) | ||||||
| { | { | ||||||
| 	const T half_fov_tan = std::tan(field_of_view / static_cast<T>(2)); | 	const T half_fov_tan = std::tan(field_of_view / static_cast<T>(2)); | ||||||
| 
 | 
 | ||||||
| 	auto result = mat4_impl<T> { T { 0 } }; | 	auto result = mat4_impl<T>::identity(); | ||||||
| 
 | 
 | ||||||
| 	result[0][0] = T { 1 } / (aspect_ratio * half_fov_tan); | 	result[0][0] = T { 1 } / (aspect_ratio * half_fov_tan); | ||||||
| 
 | 	//
 | ||||||
| 	result[1][1] = T { 1 } / (half_fov_tan); | 	result[1][1] = T { 1 } / (half_fov_tan); | ||||||
| 
 | 	//
 | ||||||
| 	result[2][2] = -(z_far + z_near) / (z_far - z_near); | 	//	result[2][2] = -(z_far + z_near) / (z_far - z_near);
 | ||||||
| 
 | 	//
 | ||||||
|  | 	result[2][2] = z_far / (z_far - z_near); | ||||||
|  | 	//
 | ||||||
| 	result[2][3] = -T { 1 }; | 	result[2][3] = -T { 1 }; | ||||||
| 
 | 	//
 | ||||||
| 	result[3][2] = -(T { 2 } * z_far * z_near) / (z_far - z_near); | 	// result[3][2] = -(T { 2 } * z_far * z_near) / (z_far - z_near);
 | ||||||
| 
 | 	result[3][2] = -(z_far * z_near) / (z_far - z_near); | ||||||
|  | 	//
 | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ template<typename T = float> | ||||||
| struct mat4_impl | struct mat4_impl | ||||||
| { | { | ||||||
| 	using Column_T = vec4_impl<T>; | 	using Column_T = vec4_impl<T>; | ||||||
|  | 
 | ||||||
| 	constexpr explicit mat4_impl(T scalar = 0) | 	constexpr explicit mat4_impl(T scalar = 0) | ||||||
| 	    : values( | 	    : values( | ||||||
| 	          { | 	          { | ||||||
|  | @ -43,7 +44,7 @@ struct mat4_impl | ||||||
| 	{ | 	{ | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] constexpr auto identity() -> mat4_impl<T> | 	[[nodiscard]] static constexpr auto identity() -> mat4_impl<T> | ||||||
| 	{ | 	{ | ||||||
| 		return mat4_impl<T> { | 		return mat4_impl<T> { | ||||||
| 			{ 1 }, {},    {},    {},    //
 | 			{ 1 }, {},    {},    {},    //
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| add_library_module(libmirror) | add_library_module(libmirror) | ||||||
| target_link_libraries(libmirror INTERFACE app time input surface renderer) | target_link_libraries(libmirror INTERFACE app time input surface renderer | ||||||
|  |                                           camera) | ||||||
| 
 | 
 | ||||||
| add_test_module( | add_test_module( | ||||||
|     libmirror layers/editor_layer.test.cpp panels/asset_browser.test.cpp |     libmirror layers/editor_layer.test.cpp panels/asset_browser.test.cpp | ||||||
|  |  | ||||||
|  | @ -2,9 +2,11 @@ | ||||||
| #include <app/application.hpp> | #include <app/application.hpp> | ||||||
| #include <app/entrypoint.hpp> | #include <app/entrypoint.hpp> | ||||||
| #include <app/system.hpp> | #include <app/system.hpp> | ||||||
|  | #include <camera/components.hpp> | ||||||
| #include <ecs/entity.hpp> | #include <ecs/entity.hpp> | ||||||
| #include <input/components.hpp> | #include <input/components.hpp> | ||||||
| #include <input/system.hpp> | #include <input/system.hpp> | ||||||
|  | #include <math/trig.hpp> | ||||||
| #include <math/vec2.hpp> | #include <math/vec2.hpp> | ||||||
| #include <memory/reference.hpp> | #include <memory/reference.hpp> | ||||||
| #include <memory/scope.hpp> | #include <memory/scope.hpp> | ||||||
|  | @ -66,13 +68,18 @@ public: | ||||||
| 			const auto &[x, y] = surface.get_position(); | 			const auto &[x, y] = surface.get_position(); | ||||||
| 			const auto &[width, height] = surface.get_resolution(); | 			const auto &[width, height] = surface.get_resolution(); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| 			if (input.get_action(m_quit_action_key).state == State::active) | 			if (input.get_action(m_quit_action_key).state == State::active) | ||||||
| 			{ | 			{ | ||||||
| 				should_quit = true; | 				should_quit = true; | ||||||
| 			} | 			} | ||||||
| 			if (input.get_action(m_debug_action_keys[0]).state == State::active) | 			if (input.get_action(m_debug_action_keys[0]).state == State::active) | ||||||
| 			{ | 			{ | ||||||
| 				surface.push_request(surface::ModifyPositionRequest({ x + 5, y + 5 })); | 				for (auto &[id, camera] : | ||||||
|  | 				     m_registry->view<lt::camera::components::PerspectiveCamera>()) | ||||||
|  | 				{ | ||||||
|  | 					camera.vertical_fov += (static_cast<float>(tick.delta_time.count()) * 40.0f); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (input.get_action(m_debug_action_keys[1]).state == State::active) | 			if (input.get_action(m_debug_action_keys[1]).state == State::active) | ||||||
|  | @ -219,6 +226,20 @@ public: | ||||||
| 		        .callback = &renderer_callback, | 		        .callback = &renderer_callback, | ||||||
| 		        .user_data = this, | 		        .user_data = this, | ||||||
| 		    } }); | 		    } }); | ||||||
|  | 
 | ||||||
|  | 		m_camera_id = m_editor_registry->create_entity(); | ||||||
|  | 
 | ||||||
|  | 		m_editor_registry->add( | ||||||
|  | 		    m_camera_id, | ||||||
|  | 		    camera::components::PerspectiveCamera { | ||||||
|  | 		        .vertical_fov = math::radians(90.0f), | ||||||
|  | 		        .near_plane = 0.1f, | ||||||
|  | 		        .far_plane = 30.0, | ||||||
|  | 		        .aspect_ratio = 1.0f, | ||||||
|  | 		        .background_color = math::vec4(1.0, 0.0, 0.0, 1.0), | ||||||
|  | 		        .is_primary = true, | ||||||
|  | 		    } | ||||||
|  | 		); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void setup_input_system() | 	void setup_input_system() | ||||||
|  | @ -245,6 +266,8 @@ private: | ||||||
| 	memory::Ref<MirrorSystem> m_mirror_system; | 	memory::Ref<MirrorSystem> m_mirror_system; | ||||||
| 
 | 
 | ||||||
| 	lt::ecs::EntityId m_window = lt::ecs::null_entity; | 	lt::ecs::EntityId m_window = lt::ecs::null_entity; | ||||||
|  | 
 | ||||||
|  | 	lt::ecs::EntityId m_camera_id = lt::ecs::null_entity; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| auto app::create_application() -> memory::Scope<app::Application> | auto app::create_application() -> memory::Scope<app::Application> | ||||||
|  |  | ||||||
|  | @ -8,21 +8,29 @@ add_library_module( | ||||||
|     backend/vk/context/instance.cpp |     backend/vk/context/instance.cpp | ||||||
|     backend/vk/context/surface.cpp |     backend/vk/context/surface.cpp | ||||||
|     backend/vk/context/swapchain.cpp |     backend/vk/context/swapchain.cpp | ||||||
|  |     backend/vk/data/buffer.cpp | ||||||
|     backend/vk/renderer/pass.cpp |     backend/vk/renderer/pass.cpp | ||||||
|     backend/vk/renderer/renderer.cpp |     backend/vk/renderer/renderer.cpp | ||||||
|     # Vulkan - frontend |     # frontend | ||||||
|     frontend/messenger.cpp |     frontend/messenger.cpp | ||||||
|     frontend/context/device.cpp |     frontend/context/device.cpp | ||||||
|     frontend/context/gpu.cpp |     frontend/context/gpu.cpp | ||||||
|     frontend/context/instance.cpp |     frontend/context/instance.cpp | ||||||
|     frontend/context/surface.cpp |     frontend/context/surface.cpp | ||||||
|     frontend/context/swapchain.cpp |     frontend/context/swapchain.cpp | ||||||
|  |     frontend/data/buffer.cpp | ||||||
|     frontend/renderer/renderer.cpp |     frontend/renderer/renderer.cpp | ||||||
|     frontend/renderer/pass.cpp) |     frontend/renderer/pass.cpp) | ||||||
| 
 | 
 | ||||||
| target_link_libraries( | target_link_libraries( | ||||||
|     renderer |     renderer | ||||||
|     PUBLIC app ecs memory assets time bitwise |     PUBLIC app | ||||||
|  |            ecs | ||||||
|  |            memory | ||||||
|  |            assets | ||||||
|  |            time | ||||||
|  |            bitwise | ||||||
|  |            camera | ||||||
|     PRIVATE surface pthread) |     PRIVATE surface pthread) | ||||||
| 
 | 
 | ||||||
| add_test_module( | add_test_module( | ||||||
|  | @ -34,11 +42,10 @@ add_test_module( | ||||||
|     frontend/context/surface.test.cpp |     frontend/context/surface.test.cpp | ||||||
|     frontend/context/device.test.cpp |     frontend/context/device.test.cpp | ||||||
|     frontend/context/swapchain.test.cpp |     frontend/context/swapchain.test.cpp | ||||||
|     frontend/renderer/pass.test.cpp |     frontend/data/buffer.test.cpp | ||||||
|  |     # frontend/renderer/pass.test.cpp | ||||||
|     frontend/renderer/renderer.test.cpp |     frontend/renderer/renderer.test.cpp | ||||||
|     # backend specific tests -- vk |     # backend specific tests -- vk | ||||||
|     backend/vk/context/instance.test.cpp |     backend/vk/context/instance.test.cpp) | ||||||
|     # backend specific tests -- dx backend specific tests -- mt |  | ||||||
| ) |  | ||||||
| 
 | 
 | ||||||
| target_link_libraries(renderer_tests PRIVATE surface pthread) | target_link_libraries(renderer_tests PRIVATE surface pthread) | ||||||
|  |  | ||||||
|  | @ -75,10 +75,17 @@ void Device::initialize_logical_device() | ||||||
| 
 | 
 | ||||||
| 	auto extensions = std::vector<const char *> { | 	auto extensions = std::vector<const char *> { | ||||||
| 		VK_KHR_SWAPCHAIN_EXTENSION_NAME, | 		VK_KHR_SWAPCHAIN_EXTENSION_NAME, | ||||||
|  | 		VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	const auto dynamic_rendering_features = VkPhysicalDeviceDynamicRenderingFeatures { | ||||||
|  | 		.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, | ||||||
|  | 		.dynamicRendering = true, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto device_info = VkDeviceCreateInfo { | 	auto device_info = VkDeviceCreateInfo { | ||||||
| 		.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | 		.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | ||||||
|  | 		.pNext = &dynamic_rendering_features, | ||||||
| 		.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size()), | 		.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size()), | ||||||
| 		.pQueueCreateInfos = queue_infos.data(), | 		.pQueueCreateInfos = queue_infos.data(), | ||||||
| 		.enabledExtensionCount = static_cast<uint32_t>(extensions.size()), | 		.enabledExtensionCount = static_cast<uint32_t>(extensions.size()), | ||||||
|  | @ -207,6 +214,31 @@ void Device::wait_for_fences(std::span<VkFence> fences) const | ||||||
| 	return images; | 	return images; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [[nodiscard]] auto Device::get_memory_requirements(VkBuffer buffer) const -> VkMemoryRequirements | ||||||
|  | { | ||||||
|  | 	auto requirements = VkMemoryRequirements {}; | ||||||
|  | 	vk_get_buffer_memory_requirements(m_device, buffer, &requirements); | ||||||
|  | 	return requirements; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Device::bind_memory(VkBuffer buffer, VkDeviceMemory memory, size_t offset /* = 0u */) const | ||||||
|  | { | ||||||
|  | 	vkc(vk_bind_buffer_memory(m_device, buffer, memory, offset)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [[nodiscard]] auto Device::map_memory(VkDeviceMemory memory, size_t size, size_t offset) const | ||||||
|  |     -> std::span<std::byte> | ||||||
|  | { | ||||||
|  | 	void *data = {}; | ||||||
|  | 	vkc(vk_map_memory(m_device, memory, offset, size, {}, &data)); | ||||||
|  | 	return { std::bit_cast<std::byte *>(data), size }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Device::unmap_memory(VkDeviceMemory memory) | ||||||
|  | { | ||||||
|  | 	vk_unmap_memory(m_device, memory); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| [[nodiscard]] auto Device::create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR | [[nodiscard]] auto Device::create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR | ||||||
| { | { | ||||||
| 	auto *swapchain = VkSwapchainKHR {}; | 	auto *swapchain = VkSwapchainKHR {}; | ||||||
|  | @ -251,9 +283,18 @@ void Device::wait_for_fences(std::span<VkFence> fences) const | ||||||
| 	return pass; | 	return pass; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| [[nodiscard]] auto Device::create_pipeline_layout(VkPipelineLayoutCreateInfo info) const | [[nodiscard]] auto Device::create_pipeline_layout( | ||||||
|     -> VkPipelineLayout |     std::vector<VkPushConstantRange> push_constant_ranges | ||||||
|  | ) const -> VkPipelineLayout | ||||||
| { | { | ||||||
|  | 	auto info = VkPipelineLayoutCreateInfo { | ||||||
|  | 		.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||||||
|  | 		.setLayoutCount = 0u, | ||||||
|  | 		.pSetLayouts = nullptr, | ||||||
|  | 		.pushConstantRangeCount = static_cast<uint32_t>(push_constant_ranges.size()), | ||||||
|  | 		.pPushConstantRanges = push_constant_ranges.data(), | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	auto *pipeline_layout = VkPipelineLayout {}; | 	auto *pipeline_layout = VkPipelineLayout {}; | ||||||
| 	vkc(vk_create_pipeline_layout(m_device, &info, nullptr, &pipeline_layout)); | 	vkc(vk_create_pipeline_layout(m_device, &info, nullptr, &pipeline_layout)); | ||||||
| 	return pipeline_layout; | 	return pipeline_layout; | ||||||
|  | @ -299,6 +340,13 @@ void Device::wait_for_fences(std::span<VkFence> fences) const | ||||||
| 	return fences; | 	return fences; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [[nodiscard]] auto Device::create_buffer(VkBufferCreateInfo info) const -> VkBuffer | ||||||
|  | { | ||||||
|  | 	auto *buffer = VkBuffer {}; | ||||||
|  | 	vkc(vk_create_buffer(m_device, &info, nullptr, &buffer)); | ||||||
|  | 	return buffer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| [[nodiscard]] auto Device::allocate_command_buffers(VkCommandBufferAllocateInfo info) const | [[nodiscard]] auto Device::allocate_command_buffers(VkCommandBufferAllocateInfo info) const | ||||||
|     -> std::vector<VkCommandBuffer> |     -> std::vector<VkCommandBuffer> | ||||||
| { | { | ||||||
|  | @ -307,6 +355,18 @@ void Device::wait_for_fences(std::span<VkFence> fences) const | ||||||
| 	return command_buffers; | 	return command_buffers; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [[nodiscard]] auto Device::allocate_memory(VkMemoryAllocateInfo info) const -> VkDeviceMemory | ||||||
|  | { | ||||||
|  | 	auto *memory = VkDeviceMemory {}; | ||||||
|  | 	vkc(vk_allocate_memory(m_device, &info, nullptr, &memory)); | ||||||
|  | 	return memory; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Device::free_memory(VkDeviceMemory memory) const | ||||||
|  | { | ||||||
|  | 	vk_free_memory(m_device, memory, nullptr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Device::destroy_swapchain(VkSwapchainKHR swapchain) const | void Device::destroy_swapchain(VkSwapchainKHR swapchain) const | ||||||
| { | { | ||||||
| 	vk_destroy_swapchain_khr(m_device, swapchain, m_allocator); | 	vk_destroy_swapchain_khr(m_device, swapchain, m_allocator); | ||||||
|  | @ -389,4 +449,9 @@ void Device::destroy_fences(std::span<VkFence> fences) const | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void Device::destroy_buffer(VkBuffer buffer) const | ||||||
|  | { | ||||||
|  | 	vk_destroy_buffer(m_device, buffer, nullptr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace lt::renderer::vk
 | } // namespace lt::renderer::vk
 | ||||||
|  |  | ||||||
|  | @ -96,6 +96,16 @@ public: | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] auto get_swapchain_images(VkSwapchainKHR swapchain) const -> std::vector<VkImage>; | 	[[nodiscard]] auto get_swapchain_images(VkSwapchainKHR swapchain) const -> std::vector<VkImage>; | ||||||
| 
 | 
 | ||||||
|  | 	[[nodiscard]] auto get_memory_requirements(VkBuffer buffer) const -> VkMemoryRequirements; | ||||||
|  | 
 | ||||||
|  | 	/** binders / mappers  */ | ||||||
|  | 	void bind_memory(VkBuffer buffer, VkDeviceMemory memory, size_t offset = 0u) const; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto map_memory(VkDeviceMemory memory, size_t size, size_t offset) const | ||||||
|  | 	    -> std::span<std::byte>; | ||||||
|  | 
 | ||||||
|  | 	void unmap_memory(VkDeviceMemory memory); | ||||||
|  | 
 | ||||||
| 	/** create functions */ | 	/** create functions */ | ||||||
| 	[[nodiscard]] auto create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR; | 	[[nodiscard]] auto create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR; | ||||||
| 
 | 
 | ||||||
|  | @ -108,8 +118,9 @@ public: | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] auto create_pass(VkRenderPassCreateInfo info) const -> VkRenderPass; | 	[[nodiscard]] auto create_pass(VkRenderPassCreateInfo info) const -> VkRenderPass; | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] auto create_pipeline_layout(VkPipelineLayoutCreateInfo info) const | 	[[nodiscard]] auto create_pipeline_layout( | ||||||
| 	    -> VkPipelineLayout; | 	    std::vector<VkPushConstantRange> push_constant_ranges | ||||||
|  | 	) const -> VkPipelineLayout; | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] auto create_shader_module(VkShaderModuleCreateInfo info) const -> VkShaderModule; | 	[[nodiscard]] auto create_shader_module(VkShaderModuleCreateInfo info) const -> VkShaderModule; | ||||||
| 
 | 
 | ||||||
|  | @ -120,10 +131,17 @@ public: | ||||||
| 	[[nodiscard]] auto create_fences(VkFenceCreateInfo info, uint32_t count) const | 	[[nodiscard]] auto create_fences(VkFenceCreateInfo info, uint32_t count) const | ||||||
| 	    -> std::vector<VkFence>; | 	    -> std::vector<VkFence>; | ||||||
| 
 | 
 | ||||||
|  | 	[[nodiscard]] auto create_buffer(VkBufferCreateInfo info) const -> VkBuffer; | ||||||
|  | 
 | ||||||
| 	/** allocation functions */ | 	/** allocation functions */ | ||||||
|  | 	[[nodiscard]] auto allocate_memory(VkMemoryAllocateInfo info) const -> VkDeviceMemory; | ||||||
|  | 
 | ||||||
| 	[[nodiscard]] auto allocate_command_buffers(VkCommandBufferAllocateInfo info) const | 	[[nodiscard]] auto allocate_command_buffers(VkCommandBufferAllocateInfo info) const | ||||||
| 	    -> std::vector<VkCommandBuffer>; | 	    -> std::vector<VkCommandBuffer>; | ||||||
| 
 | 
 | ||||||
|  | 	/** de-allocation functions */ | ||||||
|  | 	void free_memory(VkDeviceMemory memory) const; | ||||||
|  | 
 | ||||||
| 	/** destroy functions */ | 	/** destroy functions */ | ||||||
| 	void destroy_swapchain(VkSwapchainKHR swapchain) const; | 	void destroy_swapchain(VkSwapchainKHR swapchain) const; | ||||||
| 
 | 
 | ||||||
|  | @ -153,6 +171,8 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void destroy_fences(std::span<VkFence> fences) const; | 	void destroy_fences(std::span<VkFence> fences) const; | ||||||
| 
 | 
 | ||||||
|  | 	void destroy_buffer(VkBuffer buffer) const; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
| 	template<typename T> | 	template<typename T> | ||||||
| 	static auto get_object_type(const T &object) -> VkObjectType | 	static auto get_object_type(const T &object) -> VkObjectType | ||||||
|  |  | ||||||
|  | @ -5,35 +5,38 @@ | ||||||
| namespace lt::renderer::vk { | namespace lt::renderer::vk { | ||||||
| 
 | 
 | ||||||
| Gpu::Gpu(IInstance *instance) | Gpu::Gpu(IInstance *instance) | ||||||
|  |     : m_descriptor_indexing_features( | ||||||
|  |           { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES } | ||||||
|  |       ) | ||||||
| { | { | ||||||
| 	auto gpus = static_cast<Instance *>(instance)->enumerate_gpus(); | 	auto gpus = static_cast<Instance *>(instance)->enumerate_gpus(); | ||||||
| 
 | 
 | ||||||
| 	for (auto &gpu : gpus) | 	for (auto &gpu : gpus) | ||||||
| 	{ | 	{ | ||||||
| 		auto properties = VkPhysicalDeviceProperties {}; | 		auto properties = VkPhysicalDeviceProperties {}; | ||||||
| 		auto features = VkPhysicalDeviceFeatures {}; | 		auto features = VkPhysicalDeviceFeatures2 { | ||||||
|  | 			.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, | ||||||
|  | 			.pNext = &m_descriptor_indexing_features | ||||||
|  | 		}; | ||||||
| 
 | 
 | ||||||
| 		vk_get_physical_device_properties(gpu, &properties); | 		vk_get_physical_device_properties(gpu, &properties); | ||||||
| 		vk_get_physical_device_features(gpu, &features); | 		vk_get_physical_device_features(gpu, &features); | ||||||
| 
 | 
 | ||||||
| 		if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU | 		if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU | ||||||
| 		    && features.geometryShader) | 		    && features.features.geometryShader) | ||||||
| 		{ | 		{ | ||||||
| 			m_gpu = gpu; | 			m_gpu = gpu; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	ensure(m_gpu, "Failed to find any suitable Vulkan physical device"); | 	ensure(m_gpu, "Failed to find any suitable Vulkan physical device"); | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| [[nodiscard]] auto Gpu::get_queue_family_properties() const -> std::vector<VkQueueFamilyProperties> | 
 | ||||||
| { | 	vk_get_physical_device_memory_properties(m_gpu, &m_memory_properties); | ||||||
|  | 
 | ||||||
| 	auto count = uint32_t { 0u }; | 	auto count = uint32_t { 0u }; | ||||||
| 	vk_get_physical_device_queue_family_properties(m_gpu, &count, nullptr); | 	vk_get_physical_device_queue_family_properties(m_gpu, &count, nullptr); | ||||||
| 
 | 	m_queue_family_properties.resize(count); | ||||||
| 	auto properties = std::vector<VkQueueFamilyProperties>(count); | 	vk_get_physical_device_queue_family_properties(m_gpu, &count, m_queue_family_properties.data()); | ||||||
| 	vk_get_physical_device_queue_family_properties(m_gpu, &count, properties.data()); |  | ||||||
| 
 |  | ||||||
| 	return properties; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| [[nodiscard]] auto Gpu::queue_family_supports_presentation( | [[nodiscard]] auto Gpu::queue_family_supports_presentation( | ||||||
|  | @ -67,5 +70,11 @@ Gpu::Gpu(IInstance *instance) | ||||||
| 	return formats; | 	return formats; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [[nodiscard]] auto Gpu::create_device(VkDeviceCreateInfo info) const -> VkDevice | ||||||
|  | { | ||||||
|  | 	auto *device = VkDevice {}; | ||||||
|  | 	vkc(vk_create_device(m_gpu, &info, nullptr, &device)); | ||||||
|  | 	return device; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| } // namespace lt::renderer::vk
 | } // namespace lt::renderer::vk
 | ||||||
|  |  | ||||||
|  | @ -12,13 +12,6 @@ class Gpu: public IGpu | ||||||
| public: | public: | ||||||
| 	Gpu(IInstance *instance); | 	Gpu(IInstance *instance); | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] auto vk() const -> VkPhysicalDevice |  | ||||||
| 	{ |  | ||||||
| 		return m_gpu; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_queue_family_properties() const -> std::vector<VkQueueFamilyProperties>; |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto queue_family_supports_presentation( | 	[[nodiscard]] auto queue_family_supports_presentation( | ||||||
| 	    VkSurfaceKHR surface, | 	    VkSurfaceKHR surface, | ||||||
| 	    uint32_t queue_family_idx | 	    uint32_t queue_family_idx | ||||||
|  | @ -30,8 +23,39 @@ public: | ||||||
| 	[[nodiscard]] auto get_surface_formats(VkSurfaceKHR surface) const | 	[[nodiscard]] auto get_surface_formats(VkSurfaceKHR surface) const | ||||||
| 	    -> std::vector<VkSurfaceFormatKHR>; | 	    -> std::vector<VkSurfaceFormatKHR>; | ||||||
| 
 | 
 | ||||||
|  | 	[[nodiscard]] auto create_device(VkDeviceCreateInfo info) const -> VkDevice; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto get_properties() const -> VkPhysicalDeviceProperties | ||||||
|  | 	{ | ||||||
|  | 		return m_properties; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto get_descriptor_indexing_features() const | ||||||
|  | 	    -> VkPhysicalDeviceDescriptorIndexingFeatures | ||||||
|  | 	{ | ||||||
|  | 		return m_descriptor_indexing_features; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto get_memory_properties() const -> VkPhysicalDeviceMemoryProperties | ||||||
|  | 	{ | ||||||
|  | 		return m_memory_properties; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto get_queue_family_properties() const -> std::vector<VkQueueFamilyProperties> | ||||||
|  | 	{ | ||||||
|  | 		return m_queue_family_properties; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
| 	memory::NullOnMove<VkPhysicalDevice> m_gpu = VK_NULL_HANDLE; | 	memory::NullOnMove<VkPhysicalDevice> m_gpu = VK_NULL_HANDLE; | ||||||
|  | 
 | ||||||
|  | 	VkPhysicalDeviceProperties m_properties {}; | ||||||
|  | 
 | ||||||
|  | 	VkPhysicalDeviceDescriptorIndexingFeatures m_descriptor_indexing_features; | ||||||
|  | 
 | ||||||
|  | 	VkPhysicalDeviceMemoryProperties m_memory_properties {}; | ||||||
|  | 
 | ||||||
|  | 	std::vector<VkQueueFamilyProperties> m_queue_family_properties; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace lt::renderer::vk
 | } // namespace lt::renderer::vk
 | ||||||
|  |  | ||||||
|  | @ -29,8 +29,9 @@ PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue_family | ||||||
| PFN_vkCreateDevice vk_create_device {}; | PFN_vkCreateDevice vk_create_device {}; | ||||||
| PFN_vkGetDeviceProcAddr vk_get_device_proc_address {}; | PFN_vkGetDeviceProcAddr vk_get_device_proc_address {}; | ||||||
| PFN_vkDestroyDevice vk_destroy_device {}; | PFN_vkDestroyDevice vk_destroy_device {}; | ||||||
| PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features {}; | PFN_vkGetPhysicalDeviceFeatures2 vk_get_physical_device_features {}; | ||||||
| PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties {}; | PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties {}; | ||||||
|  | PFN_vkGetPhysicalDeviceMemoryProperties vk_get_physical_device_memory_properties {}; | ||||||
| 
 | 
 | ||||||
| // extension instance functions
 | // extension instance functions
 | ||||||
| PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label {}; | PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label {}; | ||||||
|  | @ -86,15 +87,37 @@ PFN_vkCmdBindPipeline vk_cmd_bind_pipeline {}; | ||||||
| PFN_vkCmdDraw vk_cmd_draw {}; | PFN_vkCmdDraw vk_cmd_draw {}; | ||||||
| PFN_vkCmdSetViewport vk_cmd_set_viewport {}; | PFN_vkCmdSetViewport vk_cmd_set_viewport {}; | ||||||
| PFN_vkCmdSetScissor vk_cmd_set_scissors {}; | PFN_vkCmdSetScissor vk_cmd_set_scissors {}; | ||||||
|  | PFN_vkCmdPushConstants vk_cmd_push_constants {}; | ||||||
|  | PFN_vkCmdCopyBuffer vk_cmd_copy_buffer {}; | ||||||
|  | 
 | ||||||
|  | PFN_vkCreateDescriptorSetLayout vk_create_descriptor_set_layout {}; | ||||||
|  | PFN_vkDestroyDescriptorSetLayout vk_destroy_descriptor_set_layout {}; | ||||||
|  | PFN_vkCreateDescriptorPool vk_create_descriptor_pool {}; | ||||||
|  | PFN_vkDestroyDescriptorPool vk_destroy_descriptor_pool {}; | ||||||
|  | PFN_vkAllocateDescriptorSets vk_allocate_descriptor_sets {}; | ||||||
|  | PFN_vkFreeDescriptorSets vk_free_descriptor_sets {}; | ||||||
|  | 
 | ||||||
|  | PFN_vkCreateBuffer vk_create_buffer {}; | ||||||
|  | PFN_vkDestroyBuffer vk_destroy_buffer {}; | ||||||
|  | PFN_vkGetBufferMemoryRequirements vk_get_buffer_memory_requirements {}; | ||||||
|  | PFN_vkAllocateMemory vk_allocate_memory {}; | ||||||
|  | PFN_vkBindBufferMemory vk_bind_buffer_memory {}; | ||||||
|  | PFN_vkMapMemory vk_map_memory {}; | ||||||
|  | PFN_vkUnmapMemory vk_unmap_memory {}; | ||||||
|  | PFN_vkFreeMemory vk_free_memory {}; | ||||||
| 
 | 
 | ||||||
| PFN_vkResetCommandBuffer vk_reset_command_buffer {}; | PFN_vkResetCommandBuffer vk_reset_command_buffer {}; | ||||||
| 
 | 
 | ||||||
|  | PFN_vkCmdBeginRendering vk_cmd_begin_rendering {}; | ||||||
|  | PFN_vkCmdEndRendering vk_cmd_end_rendering {}; | ||||||
|  | 
 | ||||||
| PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {}; | PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {}; | ||||||
| PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {}; | PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {}; | ||||||
| PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {}; | PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {}; | ||||||
| 
 | 
 | ||||||
| auto vk_create_xlib_surface_khr = PFN_vkCreateXlibSurfaceKHR {}; | auto vk_create_xlib_surface_khr = PFN_vkCreateXlibSurfaceKHR {}; | ||||||
| auto vk_destroy_surface_khr = PFN_vkDestroySurfaceKHR {}; | auto vk_destroy_surface_khr = PFN_vkDestroySurfaceKHR {}; | ||||||
|  | 
 | ||||||
| // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
 | // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
 | ||||||
| 
 | 
 | ||||||
| Instance::Instance() | Instance::Instance() | ||||||
|  | @ -127,6 +150,7 @@ void Instance::initialize_instance() | ||||||
| 		VK_EXT_DEBUG_UTILS_EXTENSION_NAME, | 		VK_EXT_DEBUG_UTILS_EXTENSION_NAME, | ||||||
| 		VK_KHR_SURFACE_EXTENSION_NAME, | 		VK_KHR_SURFACE_EXTENSION_NAME, | ||||||
| 		VK_KHR_XLIB_SURFACE_EXTENSION_NAME, | 		VK_KHR_XLIB_SURFACE_EXTENSION_NAME, | ||||||
|  | 		VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const char *layer_name = "VK_LAYER_KHRONOS_validation"; | 	const char *layer_name = "VK_LAYER_KHRONOS_validation"; | ||||||
|  | @ -298,6 +322,7 @@ void Instance::load_instance_functions() | ||||||
| 	load_fn(vk_destroy_device, "vkDestroyDevice"); | 	load_fn(vk_destroy_device, "vkDestroyDevice"); | ||||||
| 	load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures"); | 	load_fn(vk_get_physical_device_features, "vkGetPhysicalDeviceFeatures"); | ||||||
| 	load_fn(vk_enumerate_device_extension_properties, "vkEnumerateDeviceExtensionProperties"); | 	load_fn(vk_enumerate_device_extension_properties, "vkEnumerateDeviceExtensionProperties"); | ||||||
|  | 	load_fn(vk_get_physical_device_memory_properties, "vkGetPhysicalDeviceMemoryProperties"); | ||||||
| 
 | 
 | ||||||
| 	load_fn(vk_cmd_begin_debug_label, "vkCmdBeginDebugUtilsLabelEXT"); | 	load_fn(vk_cmd_begin_debug_label, "vkCmdBeginDebugUtilsLabelEXT"); | ||||||
| 	load_fn(vk_cmd_end_debug_label, "vkCmdEndDebugUtilsLabelEXT"); | 	load_fn(vk_cmd_end_debug_label, "vkCmdEndDebugUtilsLabelEXT"); | ||||||
|  | @ -370,7 +395,26 @@ void Instance::load_device_functions_impl(VkDevice device) | ||||||
| 	load_fn(vk_cmd_draw, "vkCmdDraw"); | 	load_fn(vk_cmd_draw, "vkCmdDraw"); | ||||||
| 	load_fn(vk_cmd_set_viewport, "vkCmdSetViewport"); | 	load_fn(vk_cmd_set_viewport, "vkCmdSetViewport"); | ||||||
| 	load_fn(vk_cmd_set_scissors, "vkCmdSetScissor"); | 	load_fn(vk_cmd_set_scissors, "vkCmdSetScissor"); | ||||||
|  | 	load_fn(vk_cmd_push_constants, "vkCmdPushConstants"); | ||||||
|  | 	load_fn(vk_cmd_copy_buffer, "vkCmdCopyBuffer"); | ||||||
|  | 	load_fn(vk_create_descriptor_set_layout, "vkCreateDescriptorSetLayout"); | ||||||
|  | 	load_fn(vk_destroy_descriptor_set_layout, "vkDestroyDescriptorSetLayout"); | ||||||
|  | 	load_fn(vk_create_descriptor_pool, "vkCreateDescriptorPool"); | ||||||
|  | 	load_fn(vk_destroy_descriptor_pool, "vkDestroyDescriptorPool"); | ||||||
|  | 	load_fn(vk_allocate_descriptor_sets, "vkAllocateDescriptorSets"); | ||||||
|  | 	load_fn(vk_free_descriptor_sets, "vkFreeDescriptorSets"); | ||||||
|  | 	load_fn(vk_create_buffer, "vkCreateBuffer"); | ||||||
|  | 	load_fn(vk_destroy_buffer, "vkDestroyBuffer"); | ||||||
|  | 	load_fn(vk_allocate_memory, "vkAllocateMemory"); | ||||||
|  | 	load_fn(vk_bind_buffer_memory, "vkBindBufferMemory"); | ||||||
|  | 	load_fn(vk_map_memory, "vkMapMemory"); | ||||||
|  | 	load_fn(vk_unmap_memory, "vkUnmapMemory"); | ||||||
|  | 	load_fn(vk_free_memory, "vkFreeMemory"); | ||||||
|  | 	load_fn(vk_get_buffer_memory_requirements, "vkGetBufferMemoryRequirements"); | ||||||
| 	load_fn(vk_reset_command_buffer, "vkResetCommandBuffer"); | 	load_fn(vk_reset_command_buffer, "vkResetCommandBuffer"); | ||||||
|  | 
 | ||||||
|  | 	load_fn(vk_cmd_begin_rendering, "vkCmdBeginRendering"); | ||||||
|  | 	load_fn(vk_cmd_end_rendering, "vkCmdEndRendering"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| auto Instance::enumerate_gpus() const -> std::vector<VkPhysicalDevice> | auto Instance::enumerate_gpus() const -> std::vector<VkPhysicalDevice> | ||||||
|  |  | ||||||
|  | @ -49,6 +49,17 @@ public: | ||||||
| 		return m_images.size(); | 		return m_images.size(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	[[nodiscard]] auto get_image_view(uint32_t idx) -> VkImageView | ||||||
|  | 	{ | ||||||
|  | 		return m_image_views[idx]; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto get_image(uint32_t idx) -> VkImage | ||||||
|  | 	{ | ||||||
|  | 		return m_images[idx]; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 	[[nodiscard]] auto create_framebuffers_for_pass(VkRenderPass pass) const | 	[[nodiscard]] auto create_framebuffers_for_pass(VkRenderPass pass) const | ||||||
| 	    -> std::vector<VkFramebuffer>; | 	    -> std::vector<VkFramebuffer>; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										106
									
								
								modules/renderer/private/backend/vk/data/buffer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								modules/renderer/private/backend/vk/data/buffer.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | ||||||
|  | #include <renderer/backend/vk/context/device.hpp> | ||||||
|  | #include <renderer/backend/vk/context/gpu.hpp> | ||||||
|  | #include <renderer/backend/vk/data/buffer.hpp> | ||||||
|  | 
 | ||||||
|  | namespace lt::renderer::vk { | ||||||
|  | 
 | ||||||
|  | Buffer::Buffer(IDevice *device, IGpu *gpu, const CreateInfo &info) | ||||||
|  |     : m_device(static_cast<Device *>(device)) | ||||||
|  |     , m_gpu(static_cast<Gpu *>(gpu)) | ||||||
|  |     , m_buffer( | ||||||
|  |           m_device, | ||||||
|  |           VkBufferCreateInfo { | ||||||
|  |               .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||||||
|  |               .size = info.size, | ||||||
|  |               .usage = to_native_usage_flags(info.usage), | ||||||
|  |               .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||||||
|  |           } | ||||||
|  |       ) | ||||||
|  |     , m_memory(m_device, m_buffer, determine_allocation_info(info.usage)) | ||||||
|  |     , m_size(info.size) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [[nodiscard]] auto Buffer::map() -> std::span<std::byte> /* override */ | ||||||
|  | { | ||||||
|  | 	return m_device->map_memory(m_memory, m_size, 0ul); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Buffer::unmap() /* override */ | ||||||
|  | { | ||||||
|  | 	m_device->unmap_memory(m_memory); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [[nodiscard]] auto Buffer::determine_allocation_info(Usage usage) const -> VkMemoryAllocateInfo | ||||||
|  | { | ||||||
|  | 	const auto requirements = m_device->get_memory_requirements(m_buffer); | ||||||
|  | 	auto memory_properties = m_gpu->get_memory_properties(); | ||||||
|  | 
 | ||||||
|  | 	const auto required_properties = to_native_memory_properties(usage); | ||||||
|  | 	auto type = 0u; | ||||||
|  | 	for (auto idx = 0; idx < memory_properties.memoryTypeCount; ++idx) | ||||||
|  | 	{ | ||||||
|  | 		const auto property_flags = memory_properties.memoryTypes[idx].propertyFlags; | ||||||
|  | 		if (has_correct_memory_type_bit(requirements.memoryTypeBits, idx) | ||||||
|  | 		    && has_required_memory_properties(required_properties, property_flags)) | ||||||
|  | 
 | ||||||
|  | 		{ | ||||||
|  | 			type = idx; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return VkMemoryAllocateInfo { | ||||||
|  | 		.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | ||||||
|  | 		.allocationSize = requirements.size, | ||||||
|  | 		.memoryTypeIndex = type, | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [[nodiscard]] auto Buffer::to_native_usage_flags(Usage usage) const -> VkBufferUsageFlags | ||||||
|  | { | ||||||
|  | 	switch (usage) | ||||||
|  | 	{ | ||||||
|  | 	case Usage::vertex: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; | ||||||
|  | 
 | ||||||
|  | 	case Usage::index: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; | ||||||
|  | 
 | ||||||
|  | 	case Usage::storage: | ||||||
|  | 		return VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; | ||||||
|  | 
 | ||||||
|  | 	case Usage::staging: return VK_BUFFER_USAGE_TRANSFER_SRC_BIT; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	std::unreachable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [[nodiscard]] auto Buffer::to_native_memory_properties(Usage usage) const -> VkMemoryPropertyFlags | ||||||
|  | { | ||||||
|  | 	switch (usage) | ||||||
|  | 	{ | ||||||
|  | 	case Usage::vertex: | ||||||
|  | 	case Usage::index: | ||||||
|  | 	case Usage::storage: return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; | ||||||
|  | 
 | ||||||
|  | 	case Usage::staging: | ||||||
|  | 		return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	std::unreachable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [[nodiscard]] auto Buffer::has_correct_memory_type_bit(uint32_t type_bits, uint32_t type_idx) const | ||||||
|  |     -> bool | ||||||
|  | { | ||||||
|  | 	return type_bits & (1 << type_idx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [[nodiscard]] auto Buffer::has_required_memory_properties( | ||||||
|  |     uint32_t required_properties, | ||||||
|  |     uint32_t property_flags | ||||||
|  | ) const -> bool | ||||||
|  | { | ||||||
|  | 	return (property_flags & required_properties) == required_properties; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace lt::renderer::vk
 | ||||||
							
								
								
									
										60
									
								
								modules/renderer/private/backend/vk/data/buffer.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								modules/renderer/private/backend/vk/data/buffer.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <renderer/backend/vk/raii/raii.hpp> | ||||||
|  | #include <renderer/frontend/data/buffer.hpp> | ||||||
|  | 
 | ||||||
|  | namespace lt::renderer::vk { | ||||||
|  | 
 | ||||||
|  | class Buffer: public IBuffer | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	Buffer(class IDevice *device, class IGpu *gpu, const CreateInfo &info); | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto map() -> std::span<std::byte> override; | ||||||
|  | 
 | ||||||
|  | 	void unmap() override; | ||||||
|  | 
 | ||||||
|  | 	// TODO(Light): this is to make copying possible.
 | ||||||
|  | 	// But it should be removed in the future,
 | ||||||
|  | 	// Right now it's not possible because: buffers can't understand CommandBuffers.
 | ||||||
|  | 	// And I'm not sure how to properly abstract over command buffers,
 | ||||||
|  | 	// before using other APIs...
 | ||||||
|  | 	[[nodiscard]] auto vk() | ||||||
|  | 	{ | ||||||
|  | 		return *m_buffer; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto get_size() const -> size_t override | ||||||
|  | 	{ | ||||||
|  | 		return m_size; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	[[nodiscard]] auto determine_allocation_info(Usage usage) const -> VkMemoryAllocateInfo; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto to_native_usage_flags(Usage usage) const -> VkBufferUsageFlags; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto to_native_memory_properties(Usage usage) const -> VkMemoryPropertyFlags; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto has_correct_memory_type_bit(uint32_t type_bits, uint32_t type_idx) const | ||||||
|  | 	    -> bool; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto has_required_memory_properties( | ||||||
|  | 	    uint32_t required_properties, | ||||||
|  | 	    uint32_t property_flags | ||||||
|  | 	) const -> bool; | ||||||
|  | 
 | ||||||
|  | 	Device *m_device {}; | ||||||
|  | 
 | ||||||
|  | 	Gpu *m_gpu {}; | ||||||
|  | 
 | ||||||
|  | 	raii::Buffer m_buffer; | ||||||
|  | 
 | ||||||
|  | 	raii::Memory m_memory; | ||||||
|  | 
 | ||||||
|  | 	// TODO(Light): should this reflect the allocation size instead?
 | ||||||
|  | 	size_t m_size {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace lt::renderer::vk
 | ||||||
|  | @ -1,10 +1,11 @@ | ||||||
| #include <memory/pointer_types/null_on_move.hpp> | #include <memory/pointer_types/null_on_move.hpp> | ||||||
|  | #include <renderer/backend/vk/context/device.hpp> | ||||||
| #include <renderer/backend/vk/context/instance.hpp> | #include <renderer/backend/vk/context/instance.hpp> | ||||||
| #include <renderer/backend/vk/vulkan.hpp> | #include <renderer/backend/vk/vulkan.hpp> | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| namespace lt::renderer::vk::raii { | namespace lt::renderer::vk::raii { | ||||||
| 
 | 
 | ||||||
| // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
 |  | ||||||
| class DebugMessenger | class DebugMessenger | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  | @ -16,13 +17,19 @@ public: | ||||||
| 
 | 
 | ||||||
| 	~DebugMessenger() | 	~DebugMessenger() | ||||||
| 	{ | 	{ | ||||||
| 		if (!m_instance) | 		if (m_instance) | ||||||
| 		{ | 		{ | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 			m_instance->destroy_messenger(m_object); | 			m_instance->destroy_messenger(m_object); | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	DebugMessenger(DebugMessenger &&) = default; | ||||||
|  | 
 | ||||||
|  | 	DebugMessenger(const DebugMessenger &) = delete; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(DebugMessenger &&) -> DebugMessenger & = default; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(const DebugMessenger &) -> DebugMessenger & = delete; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	memory::NullOnMove<Instance *> m_instance {}; | 	memory::NullOnMove<Instance *> m_instance {}; | ||||||
|  | @ -30,4 +37,88 @@ private: | ||||||
| 	VkDebugUtilsMessengerEXT m_object; | 	VkDebugUtilsMessengerEXT m_object; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | class Buffer | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	Buffer(Device *device, VkBufferCreateInfo info) | ||||||
|  | 	    : m_device(device) | ||||||
|  | 	    , m_object(m_device->create_buffer(info)) | ||||||
|  | 	{ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	~Buffer() | ||||||
|  | 	{ | ||||||
|  | 		if (m_device) | ||||||
|  | 		{ | ||||||
|  | 			m_device->destroy_buffer(m_object); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Buffer(Buffer &&) = default; | ||||||
|  | 
 | ||||||
|  | 	Buffer(const Buffer &) = delete; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(Buffer &&) -> Buffer & = default; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(const Buffer &) -> Buffer & = delete; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto operator*() const -> VkBuffer | ||||||
|  | 	{ | ||||||
|  | 		return m_object; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] operator VkBuffer() const | ||||||
|  | 	{ | ||||||
|  | 		return m_object; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	memory::NullOnMove<Device *> m_device {}; | ||||||
|  | 
 | ||||||
|  | 	VkBuffer m_object; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Memory | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	Memory(Device *device, VkBuffer buffer, VkMemoryAllocateInfo info) | ||||||
|  | 	    : m_device(device) | ||||||
|  | 	    , m_object(m_device->allocate_memory(info)) | ||||||
|  | 	{ | ||||||
|  | 		m_device->bind_memory(buffer, m_object); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	~Memory() | ||||||
|  | 	{ | ||||||
|  | 		if (m_device) | ||||||
|  | 		{ | ||||||
|  | 			m_device->free_memory(m_object); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Memory(Memory &&) = default; | ||||||
|  | 
 | ||||||
|  | 	Memory(const Memory &) = delete; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(Memory &&) -> Memory & = default; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(const Memory &) -> Memory & = delete; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] auto operator*() const -> VkDeviceMemory | ||||||
|  | 	{ | ||||||
|  | 		return m_object; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] operator VkDeviceMemory() const | ||||||
|  | 	{ | ||||||
|  | 		return m_object; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	memory::NullOnMove<Device *> m_device {}; | ||||||
|  | 
 | ||||||
|  | 	VkDeviceMemory m_object = VK_NULL_HANDLE; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } // namespace lt::renderer::vk::raii
 | } // namespace lt::renderer::vk::raii
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #include <renderer/backend/vk/context/device.hpp> | #include <renderer/backend/vk/context/device.hpp> | ||||||
| #include <renderer/backend/vk/context/swapchain.hpp> | #include <renderer/backend/vk/context/swapchain.hpp> | ||||||
| #include <renderer/backend/vk/renderer/pass.hpp> | #include <renderer/backend/vk/renderer/pass.hpp> | ||||||
|  | #include <renderer/data/frame_constants.hpp> | ||||||
| 
 | 
 | ||||||
| namespace lt::renderer::vk { | namespace lt::renderer::vk { | ||||||
| 
 | 
 | ||||||
|  | @ -12,12 +13,12 @@ Pass::Pass( | ||||||
| ) | ) | ||||||
|     : m_device(static_cast<Device *>(device)) |     : m_device(static_cast<Device *>(device)) | ||||||
|     , m_layout(m_device->create_pipeline_layout( |     , m_layout(m_device->create_pipeline_layout( | ||||||
|           VkPipelineLayoutCreateInfo { |           std::vector<VkPushConstantRange> { | ||||||
|               .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, |               VkPushConstantRange { | ||||||
|               .setLayoutCount = 0u, |                   .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, | ||||||
|               .pSetLayouts = nullptr, |                   .offset = 0u, | ||||||
|               .pushConstantRangeCount = 0u, |                   .size = sizeof(FrameConstants), | ||||||
|               .pPushConstantRanges = nullptr, |               }, | ||||||
|           } |           } | ||||||
|       )) |       )) | ||||||
| { | { | ||||||
|  | @ -112,17 +113,16 @@ Pass::Pass( | ||||||
| 		.blendConstants = { 0.0f, 0.0, 0.0, 0.0 }, | 		.blendConstants = { 0.0f, 0.0, 0.0, 0.0 }, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 
 | 	// auto attachment_description = VkAttachmentDescription {
 | ||||||
| 	auto attachment_description = VkAttachmentDescription { | 	// 	.format =,
 | ||||||
| 		.format = static_cast<Swapchain *>(swapchain)->get_format(), | 	// 	.samples = VK_SAMPLE_COUNT_1_BIT,
 | ||||||
| 		.samples = VK_SAMPLE_COUNT_1_BIT, | 	// 	.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
 | ||||||
| 		.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, | 	// 	.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
 | ||||||
| 		.storeOp = VK_ATTACHMENT_STORE_OP_STORE, | 	// 	.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
 | ||||||
| 		.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, | 	// 	.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
 | ||||||
| 		.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | 	// 	.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
 | ||||||
| 		.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | 	// 	.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
 | ||||||
| 		.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, | 	// };
 | ||||||
| 	}; |  | ||||||
| 
 | 
 | ||||||
| 	auto color_attachment_ref = VkAttachmentReference { | 	auto color_attachment_ref = VkAttachmentReference { | ||||||
| 		.attachment = 0, | 		.attachment = 0, | ||||||
|  | @ -144,21 +144,18 @@ Pass::Pass( | ||||||
| 		.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, | 		.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	m_pass = m_device->create_pass( | 	auto color_format = static_cast<Swapchain *>(swapchain)->get_format(); | ||||||
| 	    VkRenderPassCreateInfo { | 	auto rendering_info = VkPipelineRenderingCreateInfoKHR { | ||||||
| 	        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | 		.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, | ||||||
| 	        .attachmentCount = 1u, | 		.colorAttachmentCount = 1u, | ||||||
| 	        .pAttachments = &attachment_description, | 		.pColorAttachmentFormats = &color_format, | ||||||
| 	        .subpassCount = 1u, | 
 | ||||||
| 	        .pSubpasses = &subpass_description, | 	}; | ||||||
| 	        .dependencyCount = 1u, |  | ||||||
| 	        .pDependencies = &pass_dependency, |  | ||||||
| 	    } |  | ||||||
| 	); |  | ||||||
| 
 | 
 | ||||||
| 	m_pipeline = m_device->create_graphics_pipeline( | 	m_pipeline = m_device->create_graphics_pipeline( | ||||||
| 	    VkGraphicsPipelineCreateInfo { | 	    VkGraphicsPipelineCreateInfo { | ||||||
| 	        .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | 	        .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||||||
|  | 	        .pNext = &rendering_info, | ||||||
| 	        .stageCount = static_cast<uint32_t>(shader_stages.size()), | 	        .stageCount = static_cast<uint32_t>(shader_stages.size()), | ||||||
| 	        .pStages = shader_stages.data(), | 	        .pStages = shader_stages.data(), | ||||||
| 	        .pVertexInputState = &vertex_input, | 	        .pVertexInputState = &vertex_input, | ||||||
|  | @ -170,15 +167,14 @@ Pass::Pass( | ||||||
| 	        .pColorBlendState = &color_blend, | 	        .pColorBlendState = &color_blend, | ||||||
| 	        .pDynamicState = &dynamic_state, | 	        .pDynamicState = &dynamic_state, | ||||||
| 	        .layout = m_layout, | 	        .layout = m_layout, | ||||||
| 	        .renderPass = m_pass, | 	        .renderPass = VK_NULL_HANDLE, | ||||||
| 	        .subpass = 0u, | 	        .subpass = 0u, | ||||||
| 	        .basePipelineHandle = VK_NULL_HANDLE, | 	        .basePipelineHandle = VK_NULL_HANDLE, | ||||||
| 	        .basePipelineIndex = -1, | 	        .basePipelineIndex = -1, | ||||||
| 	    } | 	    } | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	m_framebuffers = static_cast<Swapchain *>(swapchain)->create_framebuffers_for_pass(m_pass); | 	// m_framebuffers = static_cast<Swapchain *>(swapchain)->create_framebuffers_for_pass(m_pass);
 | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| 	m_device->destroy_shader_module(vertex_module); | 	m_device->destroy_shader_module(vertex_module); | ||||||
| 	m_device->destroy_shader_module(fragment_module); | 	m_device->destroy_shader_module(fragment_module); | ||||||
|  | @ -194,7 +190,7 @@ Pass::~Pass() | ||||||
| 	m_device->wait_idle(); | 	m_device->wait_idle(); | ||||||
| 	m_device->destroy_framebuffers(m_framebuffers); | 	m_device->destroy_framebuffers(m_framebuffers); | ||||||
| 	m_device->destroy_pipeline(m_pipeline); | 	m_device->destroy_pipeline(m_pipeline); | ||||||
| 	m_device->destroy_pass(m_pass); | 	// m_device->destroy_pass(m_pass);
 | ||||||
| 	m_device->destroy_pipeline_layout(m_layout); | 	m_device->destroy_pipeline_layout(m_layout); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -207,7 +203,8 @@ void Pass::replace_swapchain(const ISwapchain &swapchain) | ||||||
| 
 | 
 | ||||||
| 	m_device->wait_idle(); | 	m_device->wait_idle(); | ||||||
| 	m_device->destroy_framebuffers(m_framebuffers); | 	m_device->destroy_framebuffers(m_framebuffers); | ||||||
| 	m_framebuffers = static_cast<const Swapchain &>(swapchain).create_framebuffers_for_pass(m_pass); | 	// m_framebuffers = static_cast<const Swapchain
 | ||||||
|  | 	// &>(swapchain).create_framebuffers_for_pass(m_pass);
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| auto Pass::create_module(lt::assets::Blob blob) -> VkShaderModule | auto Pass::create_module(lt::assets::Blob blob) -> VkShaderModule | ||||||
|  |  | ||||||
|  | @ -29,16 +29,16 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void replace_swapchain(const ISwapchain &swapchain); | 	void replace_swapchain(const ISwapchain &swapchain); | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] auto get_pass() -> VkRenderPass |  | ||||||
| 	{ |  | ||||||
| 		return m_pass; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_pipeline() -> VkPipeline | 	[[nodiscard]] auto get_pipeline() -> VkPipeline | ||||||
| 	{ | 	{ | ||||||
| 		return m_pipeline; | 		return m_pipeline; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	[[nodiscard]] auto get_layout() -> VkPipelineLayout | ||||||
|  | 	{ | ||||||
|  | 		return m_layout; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	[[nodiscard]] auto get_framebuffers() -> std::vector<VkFramebuffer> & | 	[[nodiscard]] auto get_framebuffers() -> std::vector<VkFramebuffer> & | ||||||
| 	{ | 	{ | ||||||
| 		return m_framebuffers; | 		return m_framebuffers; | ||||||
|  | @ -49,8 +49,6 @@ private: | ||||||
| 
 | 
 | ||||||
| 	memory::NullOnMove<class Device *> m_device {}; | 	memory::NullOnMove<class Device *> m_device {}; | ||||||
| 
 | 
 | ||||||
| 	VkRenderPass m_pass = VK_NULL_HANDLE; |  | ||||||
| 
 |  | ||||||
| 	VkPipeline m_pipeline = VK_NULL_HANDLE; | 	VkPipeline m_pipeline = VK_NULL_HANDLE; | ||||||
| 
 | 
 | ||||||
| 	VkPipelineLayout m_layout = VK_NULL_HANDLE; | 	VkPipelineLayout m_layout = VK_NULL_HANDLE; | ||||||
|  |  | ||||||
|  | @ -100,6 +100,7 @@ Renderer::~Renderer() | ||||||
| 	vk_reset_command_buffer(cmd, {}); | 	vk_reset_command_buffer(cmd, {}); | ||||||
| 	record_cmd(cmd, *image_idx); | 	record_cmd(cmd, *image_idx); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| 	auto wait_stage = VkPipelineStageFlags { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; | 	auto wait_stage = VkPipelineStageFlags { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; | ||||||
| 	auto &submit_semaphore = m_submit_semaphores[*image_idx]; | 	auto &submit_semaphore = m_submit_semaphores[*image_idx]; | ||||||
| 	m_device->submit( | 	m_device->submit( | ||||||
|  | @ -142,35 +143,52 @@ void Renderer::replace_swapchain(ISwapchain *swapchain) | ||||||
| 
 | 
 | ||||||
| void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx) | void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx) | ||||||
| { | { | ||||||
| 	auto cmd_begin_info = VkCommandBufferBeginInfo { | 	const auto cmd_begin_info = VkCommandBufferBeginInfo { | ||||||
| 		.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | 		.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | ||||||
| 		.flags = {}, | 		.flags = {}, | ||||||
| 		.pInheritanceInfo = nullptr, | 		.pInheritanceInfo = nullptr, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	vkc(vk_begin_command_buffer(cmd, &cmd_begin_info)); | 	const auto begin_frame_barrier = VkImageMemoryBarrier { | ||||||
|  | 		.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||||||
|  | 		.srcAccessMask = {}, | ||||||
|  | 		.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, | ||||||
|  | 		.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||||||
|  | 		.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | ||||||
|  |         .image = m_swapchain->get_image(image_idx), | ||||||
|  |         .subresourceRange = VkImageSubresourceRange{ | ||||||
|  |             .aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT, | ||||||
|  |             .baseMipLevel   = 0u, | ||||||
|  |             .levelCount     = VK_REMAINING_MIP_LEVELS, | ||||||
|  |             .baseArrayLayer = 0u, | ||||||
|  |             .layerCount     = VK_REMAINING_ARRAY_LAYERS, | ||||||
|  |         }, | ||||||
| 
 | 
 | ||||||
| 	auto clear_value = VkClearValue { | 	}; | ||||||
| 			.color = {  | 
 | ||||||
|                 0.93, | 	const auto end_frame_barrier = VkImageMemoryBarrier { | ||||||
|                 0.93, | 		.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||||||
|                 0.93, | 		.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, | ||||||
|                1.0, | 		.dstAccessMask = {}, | ||||||
|  | 		.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | ||||||
|  | 		.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, | ||||||
|  |         .image = m_swapchain->get_image(image_idx), | ||||||
|  | 
 | ||||||
|  |         .subresourceRange = VkImageSubresourceRange{ | ||||||
|  |             .aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT, | ||||||
|  |             .baseMipLevel   = 0u, | ||||||
|  |             .levelCount     = VK_REMAINING_MIP_LEVELS, | ||||||
|  |             .baseArrayLayer = 0u, | ||||||
|  |             .layerCount     = VK_REMAINING_ARRAY_LAYERS, | ||||||
|         }, |         }, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	auto pass_begin_info = VkRenderPassBeginInfo { | 	const auto scissor = VkRect2D { | ||||||
| 		.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | 		.offset = { .x = 0u, .y = 0u }, | ||||||
| 		.renderPass = m_pass->get_pass(), | 		.extent = m_resolution, | ||||||
| 		.framebuffer = m_pass->get_framebuffers()[image_idx], |  | ||||||
| 		.renderArea = { .offset = {}, .extent = m_resolution }, |  | ||||||
| 		.clearValueCount = 1u, |  | ||||||
| 		.pClearValues = &clear_value |  | ||||||
| 	}; | 	}; | ||||||
| 	vk_cmd_begin_render_pass(cmd, &pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); |  | ||||||
| 	vk_cmd_bind_pipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pass->get_pipeline()); |  | ||||||
| 
 | 
 | ||||||
| 	auto viewport = VkViewport { | 	const auto viewport = VkViewport { | ||||||
| 		.x = 0.0f, | 		.x = 0.0f, | ||||||
| 		.y = 0.0f, | 		.y = 0.0f, | ||||||
| 		.width = static_cast<float>(m_resolution.width), | 		.width = static_cast<float>(m_resolution.width), | ||||||
|  | @ -178,17 +196,70 @@ void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx) | ||||||
| 		.minDepth = 0.0f, | 		.minDepth = 0.0f, | ||||||
| 		.maxDepth = 1.0f, | 		.maxDepth = 1.0f, | ||||||
| 	}; | 	}; | ||||||
| 	vk_cmd_set_viewport(cmd, 0, 1, &viewport); |  | ||||||
| 
 | 
 | ||||||
| 	auto scissor = VkRect2D { | 	const auto color_attachment_info = VkRenderingAttachmentInfoKHR { | ||||||
| 		.offset = { 0u, 0u }, | 		.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, | ||||||
| 		.extent = m_resolution, | 		.imageView = m_swapchain->get_image_view(image_idx), | ||||||
|  | 		.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | ||||||
|  | 		.resolveMode = VK_RESOLVE_MODE_NONE, | ||||||
|  | 		.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, | ||||||
|  | 		.storeOp = VK_ATTACHMENT_STORE_OP_STORE, | ||||||
|  | 		.clearValue = VkClearValue { .color = { 0.93, 0.93, 0.93, 1.0 } }, | ||||||
| 	}; | 	}; | ||||||
| 	vk_cmd_set_scissors(cmd, 0, 1, &scissor); |  | ||||||
| 
 | 
 | ||||||
|  | 	const auto rendering_info = VkRenderingInfoKHR { | ||||||
|  | 		.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR, | ||||||
|  | 		.renderArea = scissor, | ||||||
|  | 		.layerCount = 1, | ||||||
|  | 		.colorAttachmentCount = 1, | ||||||
|  | 		.pColorAttachments = &color_attachment_info, | ||||||
|  | 
 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	vkc(vk_begin_command_buffer(cmd, &cmd_begin_info)); | ||||||
|  | 	vk_cmd_push_constants( | ||||||
|  | 	    cmd, | ||||||
|  | 	    m_pass->get_layout(), | ||||||
|  | 	    VK_SHADER_STAGE_VERTEX_BIT, | ||||||
|  | 	    0u, | ||||||
|  | 	    sizeof(FrameConstants), | ||||||
|  | 	    &m_frame_constants | ||||||
|  | 	); | ||||||
|  | 	vk_cmd_pipeline_barrier( | ||||||
|  | 	    cmd, | ||||||
|  | 	    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||||||
|  | 	    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||||||
|  | 	    0, | ||||||
|  | 	    0, | ||||||
|  | 	    nullptr, | ||||||
|  | 	    0, | ||||||
|  | 	    nullptr, | ||||||
|  | 	    1, | ||||||
|  | 	    &begin_frame_barrier | ||||||
|  | 	); | ||||||
|  | 	vk_cmd_begin_rendering(cmd, &rendering_info); | ||||||
|  | 	vk_cmd_bind_pipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pass->get_pipeline()); | ||||||
|  | 	vk_cmd_set_viewport(cmd, 0, 1, &viewport); | ||||||
|  | 	vk_cmd_set_scissors(cmd, 0, 1, &scissor); | ||||||
| 	vk_cmd_draw(cmd, 3, 1, 0, 0); | 	vk_cmd_draw(cmd, 3, 1, 0, 0); | ||||||
| 	vk_cmd_end_render_pass(cmd); | 	vk_cmd_end_rendering(cmd); | ||||||
|  | 	vk_cmd_pipeline_barrier( | ||||||
|  | 	    cmd, | ||||||
|  | 	    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||||||
|  | 	    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, | ||||||
|  | 	    0, | ||||||
|  | 	    0, | ||||||
|  | 	    nullptr, | ||||||
|  | 	    0, | ||||||
|  | 	    nullptr, | ||||||
|  | 	    1, | ||||||
|  | 	    &end_frame_barrier | ||||||
|  | 	); | ||||||
| 	vkc(vk_end_command_buffer(cmd)); | 	vkc(vk_end_command_buffer(cmd)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void submit_sprite(const components::Sprite &sprite, const math::components::Transform &transform) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace lt::renderer::vk
 | } // namespace lt::renderer::vk
 | ||||||
|  |  | ||||||
|  | @ -3,8 +3,10 @@ | ||||||
| #include <memory/reference.hpp> | #include <memory/reference.hpp> | ||||||
| #include <ranges> | #include <ranges> | ||||||
| #include <renderer/backend/vk/context/device.hpp> | #include <renderer/backend/vk/context/device.hpp> | ||||||
|  | #include <renderer/backend/vk/data/buffer.hpp> | ||||||
| #include <renderer/backend/vk/renderer/pass.hpp> | #include <renderer/backend/vk/renderer/pass.hpp> | ||||||
| #include <renderer/backend/vk/utils.hpp> | #include <renderer/backend/vk/utils.hpp> | ||||||
|  | #include <renderer/frontend/data/buffer.hpp> | ||||||
| #include <renderer/frontend/renderer/pass.hpp> | #include <renderer/frontend/renderer/pass.hpp> | ||||||
| #include <renderer/frontend/renderer/renderer.hpp> | #include <renderer/frontend/renderer/renderer.hpp> | ||||||
| 
 | 
 | ||||||
|  | @ -29,6 +31,18 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void replace_swapchain(ISwapchain *swapchain) override; | 	void replace_swapchain(ISwapchain *swapchain) override; | ||||||
| 
 | 
 | ||||||
|  | 	void set_frame_constants(FrameConstants constants) override | ||||||
|  | 	{ | ||||||
|  | 		m_frame_constants = constants; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void submit_sprite( | ||||||
|  | 	    const components::Sprite &sprite, | ||||||
|  | 	    const math::components::Transform &transform | ||||||
|  | 	) override | ||||||
|  | 	{ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
| 	void record_cmd(VkCommandBuffer cmd, uint32_t image_idx); | 	void record_cmd(VkCommandBuffer cmd, uint32_t image_idx); | ||||||
| 
 | 
 | ||||||
|  | @ -51,6 +65,8 @@ private: | ||||||
| 	VkExtent2D m_resolution; | 	VkExtent2D m_resolution; | ||||||
| 
 | 
 | ||||||
| 	uint32_t m_max_frames_in_flight {}; | 	uint32_t m_max_frames_in_flight {}; | ||||||
|  | 
 | ||||||
|  | 	FrameConstants m_frame_constants; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace lt::renderer::vk
 | } // namespace lt::renderer::vk
 | ||||||
|  |  | ||||||
|  | @ -1,18 +0,0 @@ | ||||||
| // template<>
 |  | ||||||
| // struct std::formatter<VkExtent2D>
 |  | ||||||
| // {
 |  | ||||||
| // 	constexpr auto parse(std::format_parse_context &context)
 |  | ||||||
| // 	{
 |  | ||||||
| // 		return context.begin();
 |  | ||||||
| // 	}
 |  | ||||||
| //
 |  | ||||||
| // 	auto format(const VkExtent2D &val, std::format_context &context) const
 |  | ||||||
| // 	{
 |  | ||||||
| // 		return std::format_to(context.out(), "{}, {}", val.width, val.height);
 |  | ||||||
| // 	}
 |  | ||||||
| // };
 |  | ||||||
| //
 |  | ||||||
| // inline auto operator==(VkExtent2D lhs, VkExtent2D rhs) -> bool
 |  | ||||||
| // {
 |  | ||||||
| // 	return lhs.width == rhs.width && lhs.height == rhs.height;
 |  | ||||||
| // }
 |  | ||||||
|  | @ -13,8 +13,9 @@ extern PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue | ||||||
| extern PFN_vkCreateDevice vk_create_device; | extern PFN_vkCreateDevice vk_create_device; | ||||||
| extern PFN_vkGetDeviceProcAddr vk_get_device_proc_address; | extern PFN_vkGetDeviceProcAddr vk_get_device_proc_address; | ||||||
| extern PFN_vkDestroyDevice vk_destroy_device; | extern PFN_vkDestroyDevice vk_destroy_device; | ||||||
| extern PFN_vkGetPhysicalDeviceFeatures vk_get_physical_device_features; | extern PFN_vkGetPhysicalDeviceFeatures2 vk_get_physical_device_features; | ||||||
| extern PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties; | extern PFN_vkEnumerateDeviceExtensionProperties vk_enumerate_device_extension_properties; | ||||||
|  | extern PFN_vkGetPhysicalDeviceMemoryProperties vk_get_physical_device_memory_properties; | ||||||
| 
 | 
 | ||||||
| // extension instance functions
 | // extension instance functions
 | ||||||
| extern PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label; | extern PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_label; | ||||||
|  | @ -75,8 +76,29 @@ extern PFN_vkCmdBindPipeline vk_cmd_bind_pipeline; | ||||||
| extern PFN_vkCmdDraw vk_cmd_draw; | extern PFN_vkCmdDraw vk_cmd_draw; | ||||||
| extern PFN_vkCmdSetViewport vk_cmd_set_viewport; | extern PFN_vkCmdSetViewport vk_cmd_set_viewport; | ||||||
| extern PFN_vkCmdSetScissor vk_cmd_set_scissors; | extern PFN_vkCmdSetScissor vk_cmd_set_scissors; | ||||||
|  | extern PFN_vkCmdPushConstants vk_cmd_push_constants; | ||||||
|  | extern PFN_vkCmdCopyBuffer vk_cmd_copy_buffer; | ||||||
|  | 
 | ||||||
|  | extern PFN_vkCreateDescriptorSetLayout vk_create_descriptor_set_layout; | ||||||
|  | extern PFN_vkDestroyDescriptorSetLayout vk_destroy_descriptor_set_layout; | ||||||
|  | extern PFN_vkCreateDescriptorPool vk_create_descriptor_pool; | ||||||
|  | extern PFN_vkDestroyDescriptorPool vk_destroy_descriptor_pool; | ||||||
|  | extern PFN_vkAllocateDescriptorSets vk_allocate_descriptor_sets; | ||||||
|  | extern PFN_vkFreeDescriptorSets vk_free_descriptor_sets; | ||||||
|  | 
 | ||||||
|  | extern PFN_vkCreateBuffer vk_create_buffer; | ||||||
|  | extern PFN_vkDestroyBuffer vk_destroy_buffer; | ||||||
|  | extern PFN_vkGetBufferMemoryRequirements vk_get_buffer_memory_requirements; | ||||||
|  | extern PFN_vkAllocateMemory vk_allocate_memory; | ||||||
|  | extern PFN_vkBindBufferMemory vk_bind_buffer_memory; | ||||||
|  | extern PFN_vkMapMemory vk_map_memory; | ||||||
|  | extern PFN_vkUnmapMemory vk_unmap_memory; | ||||||
|  | extern PFN_vkFreeMemory vk_free_memory; | ||||||
| 
 | 
 | ||||||
| extern PFN_vkResetCommandBuffer vk_reset_command_buffer; | extern PFN_vkResetCommandBuffer vk_reset_command_buffer; | ||||||
|  | 
 | ||||||
|  | extern PFN_vkCmdBeginRendering vk_cmd_begin_rendering; | ||||||
|  | extern PFN_vkCmdEndRendering vk_cmd_end_rendering; | ||||||
| // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
 | // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
 | ||||||
| 
 | 
 | ||||||
| } // namespace lt::renderer::vk
 | } // namespace lt::renderer::vk
 | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								modules/renderer/private/frontend/data/buffer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								modules/renderer/private/frontend/data/buffer.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | #include <renderer/backend/vk/data/buffer.hpp> | ||||||
|  | #include <renderer/frontend/data/buffer.hpp> | ||||||
|  | 
 | ||||||
|  | namespace lt::renderer { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | [[nodiscard]] /* static */ auto IBuffer::create( | ||||||
|  |     Api target_api, | ||||||
|  |     class IDevice *device, | ||||||
|  |     class IGpu *gpu, | ||||||
|  |     const CreateInfo &info | ||||||
|  | ) -> memory::Scope<IBuffer> | ||||||
|  | { | ||||||
|  | 	ensure(device, "Failed to create renderer::IBuffer: null device"); | ||||||
|  | 	ensure(gpu, "Failed to create renderer::IBuffer: null gpu"); | ||||||
|  | 	ensure(info.size > 0, "Failed to create renderer::IBuffer: null size"); | ||||||
|  | 
 | ||||||
|  | 	switch (target_api) | ||||||
|  | 	{ | ||||||
|  | 	case Api::vulkan: return memory::create_scope<vk::Buffer>(device, gpu, info); | ||||||
|  | 	case Api::none: | ||||||
|  | 	case Api::metal: | ||||||
|  | 	case Api::direct_x: throw std::runtime_error { "Invalid API" }; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace lt::renderer
 | ||||||
							
								
								
									
										66
									
								
								modules/renderer/private/frontend/data/buffer.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								modules/renderer/private/frontend/data/buffer.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory/scope.hpp> | ||||||
|  | #include <renderer/api.hpp> | ||||||
|  | 
 | ||||||
|  | namespace lt::renderer { | ||||||
|  | 
 | ||||||
|  | class IBuffer | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	enum class Usage : uint8_t | ||||||
|  | 	{ | ||||||
|  | 		vertex, | ||||||
|  | 
 | ||||||
|  | 		index, | ||||||
|  | 
 | ||||||
|  | 		storage, | ||||||
|  | 
 | ||||||
|  | 		staging, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct CreateInfo | ||||||
|  | 	{ | ||||||
|  | 		Usage usage; | ||||||
|  | 
 | ||||||
|  | 		size_t size; | ||||||
|  | 
 | ||||||
|  | 		std::string debug_name; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct CopyInfo | ||||||
|  | 	{ | ||||||
|  | 		size_t offset; | ||||||
|  | 
 | ||||||
|  | 		size_t size; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] static auto create( | ||||||
|  | 	    Api target_api, | ||||||
|  | 	    class IDevice *device, | ||||||
|  | 	    class IGpu *gpu, | ||||||
|  | 	    const CreateInfo &info | ||||||
|  | 	) -> memory::Scope<IBuffer>; | ||||||
|  | 
 | ||||||
|  | 	IBuffer() = default; | ||||||
|  | 
 | ||||||
|  | 	virtual ~IBuffer() = default; | ||||||
|  | 
 | ||||||
|  | 	IBuffer(IBuffer &&) = default; | ||||||
|  | 
 | ||||||
|  | 	IBuffer(const IBuffer &) = delete; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(IBuffer &&) -> IBuffer & = default; | ||||||
|  | 
 | ||||||
|  | 	auto operator=(const IBuffer &) -> IBuffer & = delete; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] virtual auto map() -> std::span<std::byte> = 0; | ||||||
|  | 
 | ||||||
|  | 	virtual void unmap() = 0; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] virtual auto get_size() const -> size_t = 0; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace lt::renderer
 | ||||||
							
								
								
									
										115
									
								
								modules/renderer/private/frontend/data/buffer.test.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								modules/renderer/private/frontend/data/buffer.test.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | ||||||
|  | #include <renderer/frontend/data/buffer.hpp> | ||||||
|  | #include <renderer/test/utils.hpp> | ||||||
|  | 
 | ||||||
|  | using ::lt::renderer::IBuffer; | ||||||
|  | using enum ::lt::renderer::IMessenger::MessageSeverity; | ||||||
|  | 
 | ||||||
|  | Suite raii = "buffer_raii"_suite = [] { | ||||||
|  | 	Case { "happy path won't throw" } = [] { | ||||||
|  | 		auto fixture = FixtureDeviceSwapchain {}; | ||||||
|  | 
 | ||||||
|  | 		for (auto idx = 0; idx <= std::to_underlying(IBuffer::Usage::staging); ++idx) | ||||||
|  | 		{ | ||||||
|  | 			ignore = IBuffer::create( | ||||||
|  | 			    lt::renderer::Api::vulkan, | ||||||
|  | 			    fixture.device(), | ||||||
|  | 			    fixture.gpu(), | ||||||
|  | 			    IBuffer::CreateInfo { | ||||||
|  | 			        .usage = static_cast<IBuffer::Usage>(idx), | ||||||
|  | 			        .size = 1000u, | ||||||
|  | 			        .debug_name = "", | ||||||
|  | 			    } | ||||||
|  | 			); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		expect_false(fixture.has_any_messages_of(error)); | ||||||
|  | 		expect_false(fixture.has_any_messages_of(warning)); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	Case { "unhappy path throws" } = [] { | ||||||
|  | 		auto fixture = FixtureDeviceSwapchain {}; | ||||||
|  | 
 | ||||||
|  | 		auto info = IBuffer::CreateInfo { | ||||||
|  | 			.usage = IBuffer::Usage::vertex, | ||||||
|  | 			.size = 10000u, | ||||||
|  | 			.debug_name = "", | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		expect_throw([&] { | ||||||
|  | 			ignore = IBuffer::create(lt::renderer::Api::vulkan, nullptr, fixture.gpu(), info); | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		expect_throw([&] { | ||||||
|  | 			ignore = IBuffer::create(lt::renderer::Api::vulkan, fixture.device(), nullptr, info); | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		expect_throw([&, info] mutable { | ||||||
|  | 			info.size = 0; | ||||||
|  | 			ignore = IBuffer::create( | ||||||
|  | 			    lt::renderer::Api::vulkan, | ||||||
|  | 			    fixture.device(), | ||||||
|  | 			    fixture.gpu(), | ||||||
|  | 			    info | ||||||
|  | 			); | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		expect_throw([&] { | ||||||
|  | 			ignore = IBuffer::create( | ||||||
|  | 			    lt::renderer::Api::direct_x, | ||||||
|  | 			    fixture.device(), | ||||||
|  | 			    fixture.gpu(), | ||||||
|  | 			    info | ||||||
|  | 			); | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		expect_throw([&] { | ||||||
|  | 			ignore = IBuffer::create( | ||||||
|  | 			    lt::renderer::Api::metal, | ||||||
|  | 			    fixture.device(), | ||||||
|  | 			    fixture.gpu(), | ||||||
|  | 			    info | ||||||
|  | 			); | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		expect_throw([&] { | ||||||
|  | 			ignore = IBuffer::create( | ||||||
|  | 			    lt::renderer::Api::none, | ||||||
|  | 			    fixture.device(), | ||||||
|  | 			    fixture.gpu(), | ||||||
|  | 			    info | ||||||
|  | 			); | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		/** Make sure the default-case was OK */ | ||||||
|  | 		ignore = IBuffer::create(lt::renderer::Api::vulkan, fixture.device(), fixture.gpu(), info); | ||||||
|  | 
 | ||||||
|  | 		expect_false(fixture.has_any_messages_of(error)); | ||||||
|  | 		expect_false(fixture.has_any_messages_of(warning)); | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Suite mapping = "buffer_mapping"_suite = [] { | ||||||
|  | 	Case { "mapping" } = [] { | ||||||
|  | 		auto fixture = FixtureDeviceSwapchain {}; | ||||||
|  | 
 | ||||||
|  | 		constexpr auto size = 1000u; | ||||||
|  | 
 | ||||||
|  | 		auto buffer = IBuffer::create( | ||||||
|  | 		    lt::renderer::Api::vulkan, | ||||||
|  | 		    fixture.device(), | ||||||
|  | 		    fixture.gpu(), | ||||||
|  | 		    IBuffer::CreateInfo { | ||||||
|  | 		        .usage = IBuffer::Usage::staging, | ||||||
|  | 		        .size = size, | ||||||
|  | 		        .debug_name = "", | ||||||
|  | 		    } | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 		auto map = buffer->map(); | ||||||
|  | 		expect_eq(map.size(), size); | ||||||
|  | 		expect_not_nullptr(map.data()); | ||||||
|  | 
 | ||||||
|  | 		expect_false(fixture.has_any_messages_of(error)); | ||||||
|  | 		expect_false(fixture.has_any_messages_of(warning)); | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  | @ -7,7 +7,7 @@ using ::lt::renderer::IMessenger; | ||||||
| 
 | 
 | ||||||
| Suite raii = "pass_raii"_suite = [] { | Suite raii = "pass_raii"_suite = [] { | ||||||
| 	Case { "happy path won't throw" } = [] { | 	Case { "happy path won't throw" } = [] { | ||||||
| 		auto fixture = Fixture_RendererSystem {}; | 		Fixture_ auto fixture = Fixture_RendererSystem {}; | ||||||
| 		auto &system = fixture.renderer_system(); | 		auto &system = fixture.renderer_system(); | ||||||
| 
 | 
 | ||||||
| 		std::ignore = lt::renderer::IPass::create( | 		std::ignore = lt::renderer::IPass::create( | ||||||
|  |  | ||||||
|  | @ -1,7 +1,10 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <math/components/transform.hpp> | ||||||
| #include <memory/scope.hpp> | #include <memory/scope.hpp> | ||||||
| #include <renderer/api.hpp> | #include <renderer/api.hpp> | ||||||
|  | #include <renderer/components/sprite.hpp> | ||||||
|  | #include <renderer/data/frame_constants.hpp> | ||||||
| 
 | 
 | ||||||
| namespace lt::renderer { | namespace lt::renderer { | ||||||
| 
 | 
 | ||||||
|  | @ -41,6 +44,13 @@ public: | ||||||
| 	[[nodiscard]] virtual auto draw(uint32_t frame_idx) -> DrawResult = 0; | 	[[nodiscard]] virtual auto draw(uint32_t frame_idx) -> DrawResult = 0; | ||||||
| 
 | 
 | ||||||
| 	virtual void replace_swapchain(class ISwapchain *swapchain) = 0; | 	virtual void replace_swapchain(class ISwapchain *swapchain) = 0; | ||||||
|  | 
 | ||||||
|  | 	virtual void set_frame_constants(FrameConstants constants) = 0; | ||||||
|  | 
 | ||||||
|  | 	virtual void submit_sprite( | ||||||
|  | 	    const components::Sprite &sprite, | ||||||
|  | 	    const math::components::Transform &transform | ||||||
|  | 	) = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace lt::renderer
 | } // namespace lt::renderer
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,8 @@ | ||||||
|  | #include <camera/components.hpp> | ||||||
|  | #include <math/algebra.hpp> | ||||||
|  | #include <math/components/transform.hpp> | ||||||
| #include <renderer/components/messenger.hpp> | #include <renderer/components/messenger.hpp> | ||||||
|  | #include <renderer/components/sprite.hpp> | ||||||
| #include <renderer/frontend/context/device.hpp> | #include <renderer/frontend/context/device.hpp> | ||||||
| #include <renderer/frontend/context/gpu.hpp> | #include <renderer/frontend/context/gpu.hpp> | ||||||
| #include <renderer/frontend/context/instance.hpp> | #include <renderer/frontend/context/instance.hpp> | ||||||
|  | @ -70,6 +74,30 @@ void System::tick(app::TickInfo tick) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	auto perspective = math::mat4::identity(); | ||||||
|  | 	for (auto [id, camera] : m_registry->view<lt::camera::components::PerspectiveCamera>()) | ||||||
|  | 	{ | ||||||
|  | 		if (camera.is_primary) | ||||||
|  | 		{ | ||||||
|  | 			perspective = math::perspective( | ||||||
|  | 			    camera.vertical_fov, | ||||||
|  | 			    camera.aspect_ratio, | ||||||
|  | 			    camera.near_plane, | ||||||
|  | 			    camera.far_plane | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// for each sprite, submit a new "model matrix"  + "color" to go into the scene's SSBO
 | ||||||
|  | 	for (auto &[id, sprite, transform] : | ||||||
|  | 	     m_registry->view<components::Sprite, math::components::Transform>()) | ||||||
|  | 	{ | ||||||
|  | 		m_renderer->submit_sprite(sprite, transform); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	m_renderer->set_frame_constants({ .view_projection = perspective }); | ||||||
| 	if (m_renderer->draw(m_frame_idx) != IRenderer::DrawResult::success) | 	if (m_renderer->draw(m_frame_idx) != IRenderer::DrawResult::success) | ||||||
| 	{ | 	{ | ||||||
| 		m_swapchain.reset(); | 		m_swapchain.reset(); | ||||||
|  |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| 
 |  | ||||||
|  | @ -148,7 +148,6 @@ public: | ||||||
| 		); | 		); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto has_any_messages() const -> bool | 	[[nodiscard]] auto has_any_messages() const -> bool | ||||||
| 	{ | 	{ | ||||||
| 		return m_user_data->m_has_any_messages; | 		return m_user_data->m_has_any_messages; | ||||||
|  |  | ||||||
							
								
								
									
										77
									
								
								modules/renderer/public/components/sprite.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								modules/renderer/public/components/sprite.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <assets/shader.hpp> | ||||||
|  | #include <math/vec3.hpp> | ||||||
|  | #include <memory/reference.hpp> | ||||||
|  | 
 | ||||||
|  | namespace lt::renderer::components { | ||||||
|  | 
 | ||||||
|  | enum class VertexFormat : uint8_t | ||||||
|  | { | ||||||
|  | 	r32_g32_b32_sfloat, | ||||||
|  | 
 | ||||||
|  | 	r32_g32_sfloat, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class VertexInputRate : uint8_t | ||||||
|  | { | ||||||
|  | 	per_vertex, | ||||||
|  | 
 | ||||||
|  | 	per_instance, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct VertexInputAttributeDescriptipn | ||||||
|  | { | ||||||
|  | 	uint32_t location; | ||||||
|  | 
 | ||||||
|  | 	uint32_t binding; | ||||||
|  | 
 | ||||||
|  | 	uint32_t offset; | ||||||
|  | 
 | ||||||
|  | 	VertexFormat format; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct VertexInputBindingDescription | ||||||
|  | { | ||||||
|  | 	uint32_t binding; | ||||||
|  | 
 | ||||||
|  | 	uint32_t stride; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** Requires a math::components::Transform component on the same entity to be functional. */ | ||||||
|  | struct Sprite | ||||||
|  | { | ||||||
|  | 	struct Vertex | ||||||
|  | 	{ | ||||||
|  | 		math::vec3 position; | ||||||
|  | 
 | ||||||
|  | 		math::vec3 color; | ||||||
|  | 
 | ||||||
|  | 		[[nodiscard]] constexpr static auto get_attributes() | ||||||
|  | 		    -> std::array<VertexInputAttributeDescriptipn, 2> | ||||||
|  | 		{ | ||||||
|  | 			return { | ||||||
|  | 				VertexInputAttributeDescriptipn { | ||||||
|  | 				    .location = 0u, | ||||||
|  | 				    .binding = 0u, | ||||||
|  | 				    .offset = offsetof(Sprite::Vertex, position), | ||||||
|  | 				    .format = VertexFormat::r32_g32_b32_sfloat, | ||||||
|  | 
 | ||||||
|  | 				}, | ||||||
|  | 
 | ||||||
|  | 				VertexInputAttributeDescriptipn { | ||||||
|  | 				    .location = 1u, | ||||||
|  | 				    .binding = 0u, | ||||||
|  | 				    .offset = offsetof(Sprite::Vertex, color), | ||||||
|  | 				    .format = VertexFormat::r32_g32_b32_sfloat, | ||||||
|  | 				}, | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	memory::Ref<assets::ShaderAsset> vertex_shader; | ||||||
|  | 
 | ||||||
|  | 	memory::Ref<assets::ShaderAsset> fragment_shader; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace lt::renderer::components
 | ||||||
							
								
								
									
										12
									
								
								modules/renderer/public/data/frame_constants.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								modules/renderer/public/data/frame_constants.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <math/mat4.hpp> | ||||||
|  | 
 | ||||||
|  | namespace lt::renderer { | ||||||
|  | 
 | ||||||
|  | struct FrameConstants | ||||||
|  | { | ||||||
|  | 	math::mat4 view_projection; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace lt::renderer
 | ||||||
|  | @ -7,7 +7,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace lt::renderer { | namespace lt::renderer { | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| class IMessenger | class IMessenger | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -55,31 +55,6 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void tick(app::TickInfo tick) override; | 	void tick(app::TickInfo tick) override; | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] auto get_surface() -> class ISurface * |  | ||||||
| 	{ |  | ||||||
| 		return m_surface.get(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_gpu() -> class IGpu * |  | ||||||
| 	{ |  | ||||||
| 		return m_gpu.get(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_device() -> class IDevice * |  | ||||||
| 	{ |  | ||||||
| 		return m_device.get(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_swapchain() -> class ISwapchain * |  | ||||||
| 	{ |  | ||||||
| 		return m_swapchain.get(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_renderer() -> class IRenderer * |  | ||||||
| 	{ |  | ||||||
| 		return m_renderer.get(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override | 	[[nodiscard]] auto get_last_tick_result() const -> const app::TickResult & override | ||||||
| 	{ | 	{ | ||||||
| 		return m_last_tick_result; | 		return m_last_tick_result; | ||||||
|  |  | ||||||
|  | @ -4,4 +4,4 @@ add_library_module(fuzz_test test.cpp fuzz.cpp) | ||||||
| target_link_libraries(test PUBLIC tbb logger) | target_link_libraries(test PUBLIC tbb logger) | ||||||
| target_link_libraries(fuzz_test PUBLIC tbb logger) | target_link_libraries(fuzz_test PUBLIC tbb logger) | ||||||
| 
 | 
 | ||||||
| add_test_module(test test.test.cpp mock.test.cpp) | add_test_module(test test.test.cpp) | ||||||
|  |  | ||||||
|  | @ -9,9 +9,9 @@ try | ||||||
| } | } | ||||||
| catch (const std::exception &exp) | catch (const std::exception &exp) | ||||||
| { | { | ||||||
| 	std::cout << "Fuzz input resulted in uncaught exception:\n"; | 	std::println("Fuzz input resulted in uncaught exception:"); | ||||||
| 	std::cout << "\texception.what: " << exp.what() << '\n'; | 	std::println("\twhat: {}", exp.what()); | ||||||
| 	std::cout << "\tinput size: " << size << '\n'; | 	std::println("\tinput size: {}", size); | ||||||
| 
 | 
 | ||||||
| 	return EXIT_FAILURE; | 	return EXIT_FAILURE; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,52 +0,0 @@ | ||||||
| #include <test/mock.hpp> |  | ||||||
| #include <test/test.hpp> |  | ||||||
| 
 |  | ||||||
| using namespace lt::test; |  | ||||||
| using namespace lt::test::mock; |  | ||||||
| 
 |  | ||||||
| class ExpensiveClass |  | ||||||
| { |  | ||||||
| private: |  | ||||||
| public: |  | ||||||
| 	virtual int expensive(std::string str, std::optional<int> opt) |  | ||||||
| 	{ |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class MockClass: public ExpensiveClass |  | ||||||
| { |  | ||||||
| public: |  | ||||||
| 	int expensive(std::string str, std::optional<int> opt) override |  | ||||||
| 	{ |  | ||||||
| 		return expensive_mock(str, opt); |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	Mock<int(std::string, std::optional<int>)> expensive_mock {}; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class ExpensiveUser |  | ||||||
| { |  | ||||||
| public: |  | ||||||
| 	ExpensiveUser(ExpensiveClass &dependency) |  | ||||||
| 	{ |  | ||||||
| 		dependency.expensive("", 10); |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| // problem #1: matcher functions should construct an invokable object to test against the indexed
 |  | ||||||
| // argument.
 |  | ||||||
| 
 |  | ||||||
| Suite raii = "mock_raii"_suite = [] { |  | ||||||
| 	Case { "happy path won't throw" } = [] { |  | ||||||
| 		auto a = std::function<int(int)> {}; |  | ||||||
| 		auto expensive = MockClass {}; |  | ||||||
| 		auto side_effect = false; |  | ||||||
| 		expensive.expensive_mock.expect("test", std::nullopt) |  | ||||||
| 		    .apply([&](auto str, auto opt) { side_effect = true; }) |  | ||||||
| 		    .returns(69); |  | ||||||
| 
 |  | ||||||
| 		auto user = ExpensiveUser { expensive }; |  | ||||||
| 	}; |  | ||||||
| }; |  | ||||||
|  | @ -1,94 +0,0 @@ | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| namespace lt::test { |  | ||||||
| 
 |  | ||||||
| template<typename _Signature> |  | ||||||
| class Mock; |  | ||||||
| 
 |  | ||||||
| template<typename Return_Type, typename... Arg_Types> |  | ||||||
| class Mock<Return_Type(Arg_Types...)> |  | ||||||
| { |  | ||||||
| public: |  | ||||||
| 	auto at_least() -> Mock & |  | ||||||
| 	{ |  | ||||||
| 		return *this; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	auto operator&&(Mock &mock) -> Mock & |  | ||||||
| 	{ |  | ||||||
| 		return mock; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	auto operator()(Arg_Types... arguments) -> Return_Type |  | ||||||
| 	{ |  | ||||||
| 		++m_call_index; |  | ||||||
| 
 |  | ||||||
| 		for (auto &side_effect : m_side_effects) |  | ||||||
| 		{ |  | ||||||
| 			side_effect(std::forward<Arg_Types>(arguments)...); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (m_return_func) |  | ||||||
| 		{ |  | ||||||
| 			return m_return_func(std::forward<Arg_Types>(arguments)...); |  | ||||||
| 		} |  | ||||||
| 		return m_return_value; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** With any arguments. */ |  | ||||||
| 	template<uint32_t counter = 1> |  | ||||||
| 	auto expect() -> Mock & |  | ||||||
| 	{ |  | ||||||
| 		m_expected_counter = counter; |  | ||||||
| 		return *this; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	auto apply(std::function<void(Arg_Types...)> side_effect) -> Mock & |  | ||||||
| 	{ |  | ||||||
| 		m_side_effects.emplace_back(std::move(side_effect)); |  | ||||||
| 		return *this; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** Returns a fixed value. */ |  | ||||||
| 	auto returns(Return_Type value) -> Mock & |  | ||||||
| 	{ |  | ||||||
| 		m_return_value = value; |  | ||||||
| 		return *this; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** Returns a value based on input. */ |  | ||||||
| 	auto returns(std::function<Return_Type(Arg_Types...)> func) -> Mock & |  | ||||||
| 	{ |  | ||||||
| 		m_return_func = std::move(func); |  | ||||||
| 		return *this; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| 	Return_Type m_return_value {}; |  | ||||||
| 
 |  | ||||||
| 	std::function<Return_Type(Arg_Types...)> m_return_func {}; |  | ||||||
| 
 |  | ||||||
| 	std::vector<std::function<void(Arg_Types...)>> m_side_effects {}; |  | ||||||
| 
 |  | ||||||
| 	uint32_t m_call_index = 0; |  | ||||||
| 
 |  | ||||||
| 	std::vector<std::pair<std::tuple<Arg_Types...>, uint32_t>> m_expected_args; |  | ||||||
| 
 |  | ||||||
| 	uint32_t m_expected_counter {}; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| namespace mock::range { |  | ||||||
| 
 |  | ||||||
| [[nodiscard]] auto is_empty() -> bool |  | ||||||
| { |  | ||||||
| 	return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| }; // namespace mock::range
 |  | ||||||
| 
 |  | ||||||
| [[nodiscard]] auto eq(auto rhs) -> bool |  | ||||||
| { |  | ||||||
| 	return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace lt::test
 |  | ||||||
|  | @ -178,21 +178,23 @@ private: | ||||||
| 		{ | 		{ | ||||||
| 		case ExecutionPolicy::normal: | 		case ExecutionPolicy::normal: | ||||||
| 		{ | 		{ | ||||||
| 			std::cout << "[-------STATS------]\n" | 			std::println("[-------STATS------]"); | ||||||
| 			          << "suites:\n" |  | ||||||
| 			          << "\ttotal: " << m_total_suite_count << '\n' |  | ||||||
| 			          << "\tpassed: " << m_passed_suite_count << '\n' |  | ||||||
| 			          << "\tfailed: " << m_failed_suite_count << '\n' |  | ||||||
| 			          << "\tmatched: " << m_matched_suite_count << '\n' |  | ||||||
| 			          << "\tskipped: " << m_skipped_suite_count << '\n' |  | ||||||
| 			          << "tests:\n" |  | ||||||
| 			          << "\ttotal: " << m_total_case_count << '\n' |  | ||||||
| 			          << "\tpassed: " << m_passed_case_count << '\n' |  | ||||||
| 			          << "\tfailed: " << m_failed_case_count << '\n' |  | ||||||
| 			          << "\tmatched: " << m_matched_case_count << '\n' |  | ||||||
| 			          << "\tskipped: " << m_skipped_case_count << '\n'; |  | ||||||
| 
 | 
 | ||||||
| 			std::cout << "________________________________________________________________\n\n\n"; | 			std::println("suites:"); | ||||||
|  | 			std::println("\ttotal: {}", m_total_suite_count); | ||||||
|  | 			std::println("\tpassed: {}", m_passed_suite_count); | ||||||
|  | 			std::println("\tfailed: {}", m_failed_suite_count); | ||||||
|  | 			std::println("\tmatched: {}", m_matched_suite_count); | ||||||
|  | 			std::println("\tskipped: {}", m_skipped_suite_count); | ||||||
|  | 
 | ||||||
|  | 			std::println("tests:"); | ||||||
|  | 			std::println("\ttotal: {}", m_total_case_count); | ||||||
|  | 			std::println("\tpassed: {}", m_passed_case_count); | ||||||
|  | 			std::println("\tfailed: {}", m_failed_case_count); | ||||||
|  | 			std::println("\tmatched: {}", m_matched_case_count); | ||||||
|  | 			std::println("\tskipped: {}", m_skipped_case_count); | ||||||
|  | 
 | ||||||
|  | 			std::println("________________________________________________________________"); | ||||||
| 
 | 
 | ||||||
| 			return m_failed_case_count; | 			return m_failed_case_count; | ||||||
| 		} | 		} | ||||||
|  | @ -214,7 +216,7 @@ private: | ||||||
| 
 | 
 | ||||||
| 	Registry() | 	Registry() | ||||||
| 	{ | 	{ | ||||||
| 		std::cout << "________________________________________________________________\n"; | 		std::println("________________________________________________________________"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] static auto instance() -> Registry & | 	[[nodiscard]] static auto instance() -> Registry & | ||||||
|  | @ -289,16 +291,16 @@ private: | ||||||
| 		} | 		} | ||||||
| 		Registry::increment_matched_case_count(); | 		Registry::increment_matched_case_count(); | ||||||
| 
 | 
 | ||||||
| 		std::cout << "[Running-----------] --> "; | 		std::println("[Running-----------] --> "); | ||||||
| 		std::cout << m_name << '\n'; | 		std::println("{}", m_name); | ||||||
| 		try | 		try | ||||||
| 		{ | 		{ | ||||||
| 			test(); | 			test(); | ||||||
| 		} | 		} | ||||||
| 		catch (const std::exception &exp) | 		catch (const std::exception &exp) | ||||||
| 		{ | 		{ | ||||||
| 			std::cout << exp.what() << "\n"; | 			std::println("{}", exp.what()); | ||||||
| 			std::cout << "[-----------FAIL !!]" << "\n\n"; | 			std::println("[-----------FAIL !!]"); | ||||||
| 			Registry::increment_failed_case_count(); | 			Registry::increment_failed_case_count(); | ||||||
| 
 | 
 | ||||||
| 			if (Registry::should_return_on_failure()) | 			if (Registry::should_return_on_failure()) | ||||||
|  | @ -310,7 +312,7 @@ private: | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		Registry::increment_passed_case_count(); | 		Registry::increment_passed_case_count(); | ||||||
| 		std::cout << "[--------SUCCESS :D]" << "\n\n"; | 		std::println("[--------SUCCESS :D]"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	std::string_view m_name; | 	std::string_view m_name; | ||||||
|  |  | ||||||
|  | @ -220,7 +220,7 @@ tar xf 'vulkansdk-linux-x86_64-1.4.328.1.tar.xz' \ | ||||||
|         -fsanitize-ignorelist=/msan/ignorelist_all_sources \ |         -fsanitize-ignorelist=/msan/ignorelist_all_sources \ | ||||||
|         -fno-omit-frame-pointer \ |         -fno-omit-frame-pointer \ | ||||||
|         -g \ |         -g \ | ||||||
|         -std=c++23 \ |         -std=c++26 \ | ||||||
|         -nostdinc++ \ |         -nostdinc++ \ | ||||||
|         -isystem /libcxx_msan/include/c++/v1/"\ |         -isystem /libcxx_msan/include/c++/v1/"\ | ||||||
| && export CFLAGS="\ | && export CFLAGS="\ | ||||||
|  | @ -233,7 +233,7 @@ tar xf 'vulkansdk-linux-x86_64-1.4.328.1.tar.xz' \ | ||||||
|     -fsanitize-memory-track-origins \ |     -fsanitize-memory-track-origins \ | ||||||
|     -fsanitize-ignorelist=/msan/ignorelist_all_sources \ |     -fsanitize-ignorelist=/msan/ignorelist_all_sources \ | ||||||
|     -g \ |     -g \ | ||||||
|     -std=c++23 \ |     -std=c++26 \ | ||||||
|     -L/msan/lib -Wl,-rpath,/msan/lib \ |     -L/msan/lib -Wl,-rpath,/msan/lib \ | ||||||
|     -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ |     -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ | ||||||
|     -lc++ \ |     -lc++ \ | ||||||
|  | @ -272,7 +272,7 @@ export CXXFLAGS="\ | ||||||
|         -fsanitize-ignorelist=/msan/ignorelist_all_sources \ |         -fsanitize-ignorelist=/msan/ignorelist_all_sources \ | ||||||
|         -fno-omit-frame-pointer \ |         -fno-omit-frame-pointer \ | ||||||
|         -g \ |         -g \ | ||||||
|         -std=c++23 \ |         -std=c++26 \ | ||||||
|         -nostdinc++ \ |         -nostdinc++ \ | ||||||
|         -isystem /libcxx_msan/include/c++/v1/"\ |         -isystem /libcxx_msan/include/c++/v1/"\ | ||||||
| && export CFLAGS="\ | && export CFLAGS="\ | ||||||
|  | @ -285,7 +285,7 @@ export CXXFLAGS="\ | ||||||
|     -fsanitize-memory-track-origins \ |     -fsanitize-memory-track-origins \ | ||||||
|     -fsanitize-ignorelist=/msan/ignorelist_all_sources \ |     -fsanitize-ignorelist=/msan/ignorelist_all_sources \ | ||||||
|     -g \ |     -g \ | ||||||
|     -std=c++23 \ |     -std=c++26 \ | ||||||
|     -L/msan/lib -Wl,-rpath,/msan/lib \ |     -L/msan/lib -Wl,-rpath,/msan/lib \ | ||||||
|     -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ |     -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ | ||||||
|     -lc++ \ |     -lc++ \ | ||||||
|  |  | ||||||
|  | @ -34,14 +34,14 @@ cmake \ | ||||||
| -fsanitize-memory-track-origins \ | -fsanitize-memory-track-origins \ | ||||||
| -g \ | -g \ | ||||||
| -fno-omit-frame-pointer \ | -fno-omit-frame-pointer \ | ||||||
| -std=c++23 \ | -std=c++26 \ | ||||||
| -nostdinc++ \ | -nostdinc++ \ | ||||||
| -isystem /libcxx_msan/include/c++/v1/" \ | -isystem /libcxx_msan/include/c++/v1/" \ | ||||||
|     -D CMAKE_EXE_LINKER_FLAGS=" \ |     -D CMAKE_EXE_LINKER_FLAGS=" \ | ||||||
| -fsanitize=memory \ | -fsanitize=memory \ | ||||||
| -fsanitize-memory-track-origins \ | -fsanitize-memory-track-origins \ | ||||||
| -g \ | -g \ | ||||||
| -std=c++23 \ | -std=c++26 \ | ||||||
| -L/msan/lib -Wl,-rpath,/msan/lib \ | -L/msan/lib -Wl,-rpath,/msan/lib \ | ||||||
| -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ | -L/libcxx_msan/lib -Wl,-rpath,/libcxx_msan/lib \ | ||||||
| -lc++ \ | -lc++ \ | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ cmake \ | ||||||
|     -D CMAKE_LINKER_TYPE=MOLD \ |     -D CMAKE_LINKER_TYPE=MOLD \ | ||||||
|     -D ENABLE_UNIT_TESTS=ON \ |     -D ENABLE_UNIT_TESTS=ON \ | ||||||
|     -D CMAKE_BUILD_TYPE=Release \ |     -D CMAKE_BUILD_TYPE=Release \ | ||||||
|     -D CMAKE_CXX_FLAGS="-std=c++23 -g -fno-omit-frame-pointer" |     -D CMAKE_CXX_FLAGS="-std=c++26 -g -fno-omit-frame-pointer" | ||||||
| 
 | 
 | ||||||
| cmake --build ./build -j"$(nproc)" | cmake --build ./build -j"$(nproc)" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ cmake \ | ||||||
|     -D CMAKE_LINKER_TYPE=MOLD \ |     -D CMAKE_LINKER_TYPE=MOLD \ | ||||||
|     -D ENABLE_UNIT_TESTS=ON \ |     -D ENABLE_UNIT_TESTS=ON \ | ||||||
|     -D CMAKE_BUILD_TYPE=Release \ |     -D CMAKE_BUILD_TYPE=Release \ | ||||||
|     -D CMAKE_CXX_FLAGS="-std=c++23 -fno-omit-frame-pointer -fno-common -g" |     -D CMAKE_CXX_FLAGS="-std=c++26 -fno-omit-frame-pointer -fno-common -g" | ||||||
| 
 | 
 | ||||||
| cmake --build ./build -j"$(nproc)" | cmake --build ./build -j"$(nproc)" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,6 +17,6 @@ cmake \ | ||||||
|     -D ENABLE_UNIT_TESTS=ON \ |     -D ENABLE_UNIT_TESTS=ON \ | ||||||
|     -D ENABLE_STATIC_ANALYSIS=ON \ |     -D ENABLE_STATIC_ANALYSIS=ON \ | ||||||
|     -D CMAKE_BUILD_TYPE=Release \ |     -D CMAKE_BUILD_TYPE=Release \ | ||||||
|     -D CMAKE_CXX_FLAGS="-std=c++23 -stdlib=libc++" |     -D CMAKE_CXX_FLAGS="-std=c++26 -stdlib=libc++" | ||||||
| 
 | 
 | ||||||
| cmake --build . -j"$(nproc)" | cmake --build . -j"$(nproc)" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue