├── .clang-format
├── .gitignore
├── .gitmodules
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── app
├── CMakeLists.txt
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ ├── sample_tex.png
│ ├── texture.frag
│ ├── texture.frag.spv
│ ├── texture.vert
│ └── texture.vert.spv
│ ├── cpp
│ ├── Engine.cpp
│ ├── Engine.h
│ ├── Renderer.cpp
│ ├── Renderer.h
│ ├── Utils.h
│ ├── VkHelper.cpp
│ ├── VkHelper.h
│ └── main.cpp
│ └── res
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ └── values
│ └── strings.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── third_party
└── CMakeLists.txt
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: Google
2 |
3 | AccessModifierOffset: -4
4 | AlignOperands: false
5 | AllowShortFunctionsOnASingleLine: Inline
6 | AlwaysBreakBeforeMultilineStrings: false
7 | ColumnLimit: 100
8 | CommentPragmas: NOLINT:.*
9 | ConstructorInitializerIndentWidth: 6
10 | ContinuationIndentWidth: 8
11 | IndentWidth: 4
12 | PenaltyBreakBeforeFirstCallParameter: 100000
13 | SpacesBeforeTrailingComments: 1
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .idea
4 | .cxx
5 | .externalNativeBuild
6 | .DS_Store
7 | /build
8 | /app/build
9 | /local.properties
10 | /captures
11 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "third_party/glm"]
2 | path = third_party/glm
3 | url = https://github.com/g-truc/glm
4 | [submodule "third_party/stb"]
5 | path = third_party/stb
6 | url = https://github.com/nothings/stb
7 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google/conduct/).
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vulkan Pre-rotation Demo
2 |
3 | ## After git clone
4 |
5 | 1. git submodule init
6 | 2. git submodule update
7 |
8 | ## What's covered?
9 |
10 | 1. Detect all surface rotations in Android 10+(easier if landscape only without resizing).
11 | 2. Handle swapchain recreation.
12 | 3. Fix the shaders in clipping space with a simple 2x2 matrix.
13 | 4. NativityActivity, AChoreographer, etc.
14 |
15 | ## What's not covered?
16 |
17 | 1. Detect surface rotation in Android Pie and below, which can be fixed by either calling vkGetPhysicalDeviceSurfaceCapabilitiesKHR or the jni Display.getRotation() once a while.
18 | 2. Fix advanced shader features like dfdx and dfdy, which can be fixed by mapping the intended derivative to +-dfdx or +-dfdy according to preTransform pushed to the shader.
19 | 3. Other miscellaneous.
20 |
21 |
--------------------------------------------------------------------------------
/app/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.4.1)
2 |
3 |
4 | add_library(vkdemo SHARED
5 | src/main/cpp/main.cpp
6 | src/main/cpp/Engine.cpp
7 | src/main/cpp/Renderer.cpp
8 | src/main/cpp/VkHelper.cpp)
9 |
10 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
11 |
12 | add_library(native_app_glue STATIC
13 | ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
14 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
15 | target_include_directories(vkdemo PRIVATE ${ANDROID_NDK}/sources/android/native_app_glue)
16 |
17 | add_subdirectory(../third_party third_party)
18 |
19 | add_definitions("-DVK_USE_PLATFORM_ANDROID_KHR")
20 |
21 | target_link_libraries(vkdemo android native_app_glue vulkan glm stb log)
22 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | defaultConfig {
6 | applicationId "com.google.vkdemo"
7 | minSdkVersion 29
8 | targetSdkVersion 29
9 | externalNativeBuild {
10 | cmake {
11 | arguments '-DANDROID_STL=c++_static'
12 | }
13 | }
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | externalNativeBuild {
22 | cmake {
23 | path "CMakeLists.txt"
24 | }
25 | }
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
14 |
15 |
21 |
22 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/assets/sample_tex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/vulkan-pre-rotation-demo/b3f836dee9d9925820e7631dc9f4b3fe77cfa56b/app/src/main/assets/sample_tex.png
--------------------------------------------------------------------------------
/app/src/main/assets/texture.frag:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #version 450
18 |
19 | layout (binding = 0) uniform sampler2D tex;
20 | layout (location = 0) in vec2 inTexPos;
21 | layout (location = 0) out vec4 outFragColor;
22 |
23 | void main() {
24 | outFragColor = texture(tex, inTexPos);
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/assets/texture.frag.spv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/vulkan-pre-rotation-demo/b3f836dee9d9925820e7631dc9f4b3fe77cfa56b/app/src/main/assets/texture.frag.spv
--------------------------------------------------------------------------------
/app/src/main/assets/texture.vert:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #version 450
18 |
19 | layout (push_constant) uniform PushConstants {
20 | mat4 mvp;
21 | mat2 preRotate;
22 | } pushConstants;
23 | layout (location = 0) in vec2 inVertPos;
24 | layout (location = 1) in vec2 inTexPos;
25 | layout (location = 0) out vec2 outTexPos;
26 |
27 | void main() {
28 | outTexPos = inTexPos;
29 | vec4 clip = pushConstants.mvp * vec4(inVertPos, 0.0, 1.0);
30 | gl_Position = vec4(pushConstants.preRotate * vec2(clip.x, clip.y), clip.z, clip.w);
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/assets/texture.vert.spv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/vulkan-pre-rotation-demo/b3f836dee9d9925820e7631dc9f4b3fe77cfa56b/app/src/main/assets/texture.vert.spv
--------------------------------------------------------------------------------
/app/src/main/cpp/Engine.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "Engine.h"
18 |
19 | #include "Utils.h"
20 |
21 | bool Engine::isReady() {
22 | std::lock_guard lock(mLock);
23 | return mIsRendererReady;
24 | }
25 |
26 | void Engine::drawFrame() {
27 | std::lock_guard lock(mLock);
28 | if (mIsRendererReady) {
29 | mRenderer.drawFrame();
30 | }
31 | }
32 |
33 | void Engine::onInitWindow(ANativeWindow* window, AAssetManager* assetManager) {
34 | ALOGD("%s", __FUNCTION__);
35 | std::lock_guard lock(mLock);
36 | mRenderer.initialize(window, assetManager);
37 | mIsRendererReady = true;
38 | }
39 |
40 | void Engine::onWindowResized(uint32_t width, uint32_t height) {
41 | ALOGD("%s", __FUNCTION__);
42 | std::lock_guard lock(mLock);
43 | if (mIsRendererReady) {
44 | mRenderer.updateSurface(width, height);
45 | }
46 | }
47 |
48 | void Engine::onTermWindow() {
49 | ALOGD("%s", __FUNCTION__);
50 | std::lock_guard lock(mLock);
51 | if (mIsRendererReady) {
52 | mRenderer.destroy();
53 | mIsRendererReady = false;
54 | }
55 | }
56 |
57 | uint32_t Engine::getDelayMillis(int64_t /*frameTimeNanos*/) {
58 | // we can play around with frameTimeNanos to add more dynamic callback delay control
59 | return kDelayMillis;
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/cpp/Engine.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 |
21 | #include
22 |
23 | #include "Renderer.h"
24 |
25 | class Engine {
26 | public:
27 | explicit Engine() : mIsRendererReady(false) {}
28 | bool isReady();
29 | void drawFrame();
30 | void onInitWindow(ANativeWindow* window, AAssetManager* assetManager);
31 | void onWindowResized(uint32_t width, uint32_t height);
32 | void onTermWindow();
33 | uint32_t getDelayMillis(int64_t frameTimeNanos);
34 |
35 | private:
36 | // mLock protects all members below
37 | std::mutex mLock;
38 | Renderer mRenderer;
39 | bool mIsRendererReady;
40 |
41 | // defer 13ms to target 60Hz on a 60Hz display or 45Hz on a 90Hz display
42 | static constexpr const uint32_t kDelayMillis = 13;
43 | };
44 |
--------------------------------------------------------------------------------
/app/src/main/cpp/Renderer.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "Renderer.h"
18 |
19 | #include
20 | #include
21 |
22 | #define STB_IMAGE_IMPLEMENTATION
23 | #include
24 |
25 | #include
26 |
27 | #include "Utils.h"
28 |
29 | struct PushConstantBlock {
30 | glm::mat4 mvp;
31 | glm::mat2 preRotate;
32 | };
33 |
34 | /* Public APIs start here */
35 | void Renderer::initialize(ANativeWindow* window, AAssetManager* assetManager) {
36 | ASSERT(assetManager);
37 | mAssetManager = assetManager;
38 |
39 | createInstance();
40 | createDevice();
41 | createSurface(window);
42 | createSwapchain(VK_NULL_HANDLE);
43 | createTextures();
44 | createDescriptorSet();
45 | createRenderPass();
46 | createGraphicsPipeline();
47 | createVertexBuffer();
48 | createCommandBuffers();
49 | createSemaphores();
50 | createFences();
51 | }
52 |
53 | void Renderer::drawFrame() {
54 | // mInflightFences are created in the signaled state, so we can wait here from the beginning.
55 | const uint32_t frameIndex = mFrameCount % kInflight;
56 | ASSERT(mVk.WaitForFences(mDevice, 1, &mInflightFences[frameIndex], VK_TRUE, kTimeout30Sec) ==
57 | VK_SUCCESS);
58 |
59 | // Need to reset fences to unsignaled state for vkQueueSubmit
60 | ASSERT(mVk.ResetFences(mDevice, 1, &mInflightFences[frameIndex]) == VK_SUCCESS);
61 |
62 | uint32_t imageIndex;
63 | ASSERT(mVk.AcquireNextImageKHR(mDevice, mSwapchain, UINT64_MAX, mAcquireSemaphores[frameIndex],
64 | VK_NULL_HANDLE, &imageIndex) == VK_SUCCESS);
65 |
66 | // Lazy allocate VkImageView and VkFramebuffer only when needed, and reuse later
67 | if (mFramebuffers[imageIndex] == VK_NULL_HANDLE) {
68 | createFramebuffer(imageIndex);
69 | }
70 |
71 | recordCommandBuffer(frameIndex, imageIndex);
72 |
73 | const VkPipelineStageFlags waitStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
74 | const VkSubmitInfo submitInfo = {
75 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
76 | .pNext = nullptr,
77 | .waitSemaphoreCount = 1,
78 | .pWaitSemaphores = &mAcquireSemaphores[frameIndex],
79 | .pWaitDstStageMask = &waitStageMask,
80 | .commandBufferCount = 1,
81 | .pCommandBuffers = &mCommandBuffers[frameIndex],
82 | .signalSemaphoreCount = 1,
83 | .pSignalSemaphores = &mRenderSemaphores[frameIndex],
84 | };
85 | ASSERT(mVk.QueueSubmit(mQueue, 1, &submitInfo, mInflightFences[frameIndex]) == VK_SUCCESS);
86 |
87 | const VkPresentInfoKHR presentInfo = {
88 | .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
89 | .pNext = nullptr,
90 | .waitSemaphoreCount = 1,
91 | .pWaitSemaphores = &mRenderSemaphores[frameIndex],
92 | .swapchainCount = 1,
93 | .pSwapchains = &mSwapchain,
94 | .pImageIndices = &imageIndex,
95 | .pResults = nullptr,
96 | };
97 | VkResult ret = mVk.QueuePresentKHR(mQueue, &presentInfo);
98 |
99 | // If there's old swapchain to be destroyed, check if we have reached the retire frame count
100 | if (mOldSwapchain != VK_NULL_HANDLE && mRetireFrame == mFrameCount) {
101 | destroyOldSwapchain();
102 | }
103 |
104 | // VK_SUBOPTIMAL_KHR shouldn't occur again within kInflight frames in the real world. If that
105 | // happens, will switch to an array later to save the old swapchain stuff
106 | if (ret == VK_SUBOPTIMAL_KHR || mFireRecreateSwapchain) {
107 | // mFireRecreateSwapchain usually comes 3 to 4 frames later after 90 degree rotation, but we
108 | // set the countdown latency to 30 to play safe
109 | if (is180Rotation() || mFireRecreateSwapchain || !(--mPreRotationLatency)) {
110 | mPreRotationLatency = kPreRotationLatency;
111 | mFireRecreateSwapchain = false;
112 | ALOGD("%s[%u][%d] - recreate swapchain", __FUNCTION__, mFrameCount, ret);
113 | std::swap(mSwapchain, mOldSwapchain);
114 | std::swap(mImages, mOldImages);
115 | std::swap(mImageViews, mOldImageViews);
116 | std::swap(mFramebuffers, mOldFramebuffers);
117 |
118 | mRetireFrame = mFrameCount + kInflight;
119 |
120 | // Recreate the new swapchain with the latest preTransform. Numbers of swapchain images,
121 | // image views and framebuffers are also allowed to change. Even the aspect ratio of the
122 | // swapchain can change, which requires us to use dynamic viewport and scissor
123 | createSwapchain(mOldSwapchain);
124 | }
125 | } else {
126 | ASSERT(ret == VK_SUCCESS);
127 | }
128 |
129 | // Increase the frame count here and log at a frame interval
130 | if (++mFrameCount % kLogInterval == 0) {
131 | ALOGD("%s[%u][%d]", __FUNCTION__, mFrameCount, ret);
132 | }
133 | }
134 |
135 | void Renderer::updateSurface(uint32_t width, uint32_t height) {
136 | if (mSurfaceWidth != width || mSurfaceHeight != height) {
137 | mFireRecreateSwapchain = true;
138 | }
139 | }
140 |
141 | void Renderer::destroy() {
142 | if (mDevice != VK_NULL_HANDLE) {
143 | mVk.DeviceWaitIdle(mDevice);
144 |
145 | // Destroy sync objects
146 | for (auto& fence : mInflightFences) {
147 | mVk.DestroyFence(mDevice, fence, nullptr);
148 | }
149 | mInflightFences.clear();
150 | for (auto& semaphore : mAcquireSemaphores) {
151 | mVk.DestroySemaphore(mDevice, semaphore, nullptr);
152 | }
153 | mAcquireSemaphores.clear();
154 | for (auto& semaphore : mRenderSemaphores) {
155 | mVk.DestroySemaphore(mDevice, semaphore, nullptr);
156 | }
157 | mRenderSemaphores.clear();
158 |
159 | // Destroy command buffers
160 | if (!mCommandBuffers.empty()) {
161 | mVk.FreeCommandBuffers(mDevice, mCommandPool, mCommandBuffers.size(),
162 | mCommandBuffers.data());
163 | }
164 | mCommandBuffers.clear();
165 | mVk.DestroyCommandPool(mDevice, mCommandPool, nullptr);
166 | mCommandPool = VK_NULL_HANDLE;
167 |
168 | // Destroy vertex buffer
169 | mVk.DestroyBuffer(mDevice, mVertexBuffer, nullptr);
170 | mVertexBuffer = VK_NULL_HANDLE;
171 | mVk.FreeMemory(mDevice, mVertexMemory, nullptr);
172 | mVertexMemory = VK_NULL_HANDLE;
173 |
174 | // Destroy graphics pipeline
175 | mVk.DestroyPipeline(mDevice, mPipeline, nullptr);
176 | mPipeline = VK_NULL_HANDLE;
177 | mVk.DestroyPipelineLayout(mDevice, mPipelineLayout, nullptr);
178 | mPipelineLayout = VK_NULL_HANDLE;
179 |
180 | // Destroy render pass
181 | mVk.DestroyRenderPass(mDevice, mRenderPass, nullptr);
182 | mRenderPass = VK_NULL_HANDLE;
183 |
184 | // Destroy descriptor sets
185 | mVk.DestroyDescriptorPool(mDevice, mDescriptorPool, nullptr);
186 | mVk.DestroyDescriptorSetLayout(mDevice, mDescriptorSetLayout, nullptr);
187 |
188 | // Destroy textures
189 | for (auto& texture : mTextures) {
190 | mVk.DestroyImageView(mDevice, texture.view, nullptr);
191 | mVk.DestroySampler(mDevice, texture.sampler, nullptr);
192 | mVk.DestroyImage(mDevice, texture.image, nullptr);
193 | mVk.FreeMemory(mDevice, texture.memory, nullptr);
194 | }
195 | mTextures.clear();
196 |
197 | // Destroy old swapchain
198 | destroyOldSwapchain();
199 |
200 | // Destroy current swapchain
201 | for (auto& imageView : mImageViews) {
202 | mVk.DestroyImageView(mDevice, imageView, nullptr);
203 | }
204 | mImageViews.clear();
205 | for (auto& framebuffer : mFramebuffers) {
206 | mVk.DestroyFramebuffer(mDevice, framebuffer, nullptr);
207 | }
208 | mFramebuffers.clear();
209 | mImages.clear();
210 | mVk.DestroySwapchainKHR(mDevice, mSwapchain, nullptr);
211 |
212 | // Destroy device
213 | mVk.DestroyDevice(mDevice, nullptr);
214 | mDevice = VK_NULL_HANDLE;
215 | }
216 |
217 | if (mInstance) {
218 | // Destroy surface
219 | mVk.DestroySurfaceKHR(mInstance, mSurface, nullptr);
220 | mSurface = VK_NULL_HANDLE;
221 |
222 | // Destroy instance
223 | mVk.DestroyInstance(mInstance, nullptr);
224 | mInstance = VK_NULL_HANDLE;
225 | }
226 |
227 | ALOGD("Successfully destroyed Vulkan renderer");
228 | }
229 |
230 | /* Private APIs start here */
231 | static bool hasExtension(const char* extension_name,
232 | const std::vector& extensions) {
233 | return std::find_if(extensions.cbegin(), extensions.cend(),
234 | [extension_name](const VkExtensionProperties& extension) {
235 | return strcmp(extension.extensionName, extension_name) == 0;
236 | }) != extensions.cend();
237 | }
238 |
239 | void Renderer::createInstance() {
240 | mVk.initializeGlobalApi();
241 |
242 | uint32_t instanceVersion = 0;
243 | ASSERT(mVk.EnumerateInstanceVersion(&instanceVersion) == VK_SUCCESS);
244 | ASSERT(instanceVersion >= VK_MAKE_VERSION(1, 1, 0));
245 |
246 | uint32_t extensionCount = 0;
247 | ASSERT(mVk.EnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr) ==
248 | VK_SUCCESS);
249 | std::vector supportedInstanceExtensions(extensionCount);
250 | ASSERT(mVk.EnumerateInstanceExtensionProperties(nullptr, &extensionCount,
251 | supportedInstanceExtensions.data()) ==
252 | VK_SUCCESS);
253 |
254 | std::vector enabledInstanceExtensions;
255 | for (const auto extension : kRequiredInstanceExtensions) {
256 | ASSERT(hasExtension(extension, supportedInstanceExtensions));
257 | enabledInstanceExtensions.push_back(extension);
258 | }
259 |
260 | const VkApplicationInfo applicationInfo = {
261 | .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
262 | .pNext = nullptr,
263 | .pApplicationName = LOG_TAG,
264 | .applicationVersion = 0,
265 | .pEngineName = nullptr,
266 | .engineVersion = 0,
267 | .apiVersion = VK_MAKE_VERSION(1, 1, 0),
268 | };
269 | const VkInstanceCreateInfo instanceInfo = {
270 | .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
271 | .pNext = nullptr,
272 | .flags = 0,
273 | .pApplicationInfo = &applicationInfo,
274 | .enabledLayerCount = 0,
275 | .ppEnabledLayerNames = nullptr,
276 | .enabledExtensionCount = static_cast(enabledInstanceExtensions.size()),
277 | .ppEnabledExtensionNames = enabledInstanceExtensions.data(),
278 | };
279 |
280 | ASSERT(mVk.CreateInstance(&instanceInfo, nullptr, &mInstance) == VK_SUCCESS);
281 | mVk.initializeInstanceApi(mInstance);
282 |
283 | ALOGD("Successfully created instance");
284 | }
285 |
286 | void Renderer::createDevice() {
287 | uint32_t gpuCount = 0;
288 | ASSERT(mVk.EnumeratePhysicalDevices(mInstance, &gpuCount, nullptr) == VK_SUCCESS);
289 | ASSERT(gpuCount);
290 | ALOGD("gpuCount = %u", gpuCount);
291 | std::vector gpus(gpuCount, VK_NULL_HANDLE);
292 | ASSERT(mVk.EnumeratePhysicalDevices(mInstance, &gpuCount, gpus.data()) == VK_SUCCESS);
293 | mGpu = gpus[0];
294 |
295 | uint32_t extensionCount = 0;
296 | ASSERT(mVk.EnumerateDeviceExtensionProperties(mGpu, nullptr, &extensionCount, nullptr) ==
297 | VK_SUCCESS);
298 | std::vector supportedDeviceExtensions(extensionCount);
299 | ASSERT(mVk.EnumerateDeviceExtensionProperties(mGpu, nullptr, &extensionCount,
300 | supportedDeviceExtensions.data()) == VK_SUCCESS);
301 |
302 | std::vector enabledDeviceExtensions;
303 | for (const auto extension : kRequiredDeviceExtensions) {
304 | ASSERT(hasExtension(extension, supportedDeviceExtensions));
305 | enabledDeviceExtensions.push_back(extension);
306 | }
307 |
308 | uint32_t queueFamilyCount = 0;
309 | mVk.GetPhysicalDeviceQueueFamilyProperties(mGpu, &queueFamilyCount, nullptr);
310 | ASSERT(queueFamilyCount);
311 | ALOGD("queueFamilyCount = %u", queueFamilyCount);
312 | std::vector queueFamilyProperties(queueFamilyCount);
313 | mVk.GetPhysicalDeviceQueueFamilyProperties(mGpu, &queueFamilyCount,
314 | queueFamilyProperties.data());
315 |
316 | uint32_t queueFamilyIndex;
317 | for (queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; ++queueFamilyIndex) {
318 | if (queueFamilyProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
319 | break;
320 | }
321 | }
322 | ASSERT(queueFamilyIndex < queueFamilyCount);
323 | mQueueFamilyIndex = queueFamilyIndex;
324 | ALOGD("queueFamilyIndex = %u", queueFamilyIndex);
325 |
326 | const float priority = 1.0F;
327 | const VkDeviceQueueCreateInfo queueCreateInfo = {
328 | .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
329 | .pNext = nullptr,
330 | .flags = 0,
331 | .queueFamilyIndex = mQueueFamilyIndex,
332 | .queueCount = 1,
333 | .pQueuePriorities = &priority,
334 | };
335 | const VkDeviceCreateInfo deviceCreateInfo = {
336 | .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
337 | .pNext = nullptr,
338 | .queueCreateInfoCount = 1,
339 | .pQueueCreateInfos = &queueCreateInfo,
340 | .enabledLayerCount = 0,
341 | .ppEnabledLayerNames = nullptr,
342 | .enabledExtensionCount = static_cast(enabledDeviceExtensions.size()),
343 | .ppEnabledExtensionNames = enabledDeviceExtensions.data(),
344 | .pEnabledFeatures = nullptr,
345 | };
346 | ASSERT(mVk.CreateDevice(mGpu, &deviceCreateInfo, nullptr, &mDevice) == VK_SUCCESS);
347 | mVk.initializeDeviceApi(mDevice);
348 |
349 | mVk.GetDeviceQueue(mDevice, mQueueFamilyIndex, 0, &mQueue);
350 |
351 | ALOGD("Successfully created device");
352 | }
353 |
354 | void Renderer::createSurface(ANativeWindow* window) {
355 | ASSERT(window);
356 |
357 | const VkAndroidSurfaceCreateInfoKHR surfaceInfo = {
358 | .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
359 | .pNext = nullptr,
360 | .flags = 0,
361 | .window = window,
362 | };
363 | ASSERT(mVk.CreateAndroidSurfaceKHR(mInstance, &surfaceInfo, nullptr, &mSurface) == VK_SUCCESS);
364 |
365 | VkBool32 surfaceSupported = VK_FALSE;
366 | ASSERT(mVk.GetPhysicalDeviceSurfaceSupportKHR(mGpu, mQueueFamilyIndex, mSurface,
367 | &surfaceSupported) == VK_SUCCESS);
368 | ASSERT(surfaceSupported == VK_TRUE);
369 |
370 | uint32_t formatCount = 0;
371 | ASSERT(mVk.GetPhysicalDeviceSurfaceFormatsKHR(mGpu, mSurface, &formatCount, nullptr) ==
372 | VK_SUCCESS);
373 | std::vector formats(formatCount);
374 | ASSERT(mVk.GetPhysicalDeviceSurfaceFormatsKHR(mGpu, mSurface, &formatCount, formats.data()) ==
375 | VK_SUCCESS);
376 |
377 | uint32_t formatIndex;
378 | for (formatIndex = 0; formatIndex < formatCount; ++formatIndex) {
379 | if (formats[formatIndex].format == VK_FORMAT_R8G8B8A8_UNORM) {
380 | break;
381 | }
382 | }
383 | ASSERT(formatIndex < formatCount);
384 | mFormat = formats[formatIndex].format;
385 | mColorSpace = formats[formatIndex].colorSpace;
386 |
387 | ALOGD("Successfully created surface");
388 | }
389 |
390 | void Renderer::createSwapchain(VkSwapchainKHR oldSwapchain) {
391 | VkSurfaceCapabilitiesKHR surfaceCapabilities;
392 | ASSERT(mVk.GetPhysicalDeviceSurfaceCapabilitiesKHR(mGpu, mSurface, &surfaceCapabilities) ==
393 | VK_SUCCESS);
394 | ALOGD("Current surface size: %dx%d\n", surfaceCapabilities.currentExtent.width,
395 | surfaceCapabilities.currentExtent.height);
396 | ALOGD("Current transform: 0x%x\n", surfaceCapabilities.currentTransform);
397 |
398 | mSurfaceWidth = mImageWidth = surfaceCapabilities.currentExtent.width;
399 | mSurfaceHeight = mImageHeight = surfaceCapabilities.currentExtent.height;
400 | mPreTransform = surfaceCapabilities.currentTransform;
401 |
402 | if (mPreTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
403 | mPreTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
404 | std::swap(mImageWidth, mImageHeight);
405 | }
406 |
407 | const VkSwapchainCreateInfoKHR swapchainCreateInfo = {
408 | .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
409 | .pNext = nullptr,
410 | .flags = 0,
411 | .surface = mSurface,
412 | .minImageCount = kReqImageCount,
413 | .imageFormat = mFormat,
414 | .imageColorSpace = mColorSpace,
415 | .imageExtent =
416 | {
417 | .width = mImageWidth,
418 | .height = mImageHeight,
419 | },
420 | .imageArrayLayers = 1,
421 | .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
422 | .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
423 | .queueFamilyIndexCount = 1,
424 | .pQueueFamilyIndices = &mQueueFamilyIndex,
425 | .preTransform = mPreTransform,
426 | .compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
427 | .presentMode = VK_PRESENT_MODE_FIFO_KHR,
428 | .clipped = VK_FALSE,
429 | .oldSwapchain = oldSwapchain,
430 | };
431 | ASSERT(mVk.CreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &mSwapchain) ==
432 | VK_SUCCESS);
433 |
434 | uint32_t imageCount = 0;
435 | ASSERT(mVk.GetSwapchainImagesKHR(mDevice, mSwapchain, &imageCount, nullptr) == VK_SUCCESS);
436 | ALOGD("Swapchain image count = %u", imageCount);
437 |
438 | mImages.resize(imageCount, VK_NULL_HANDLE);
439 | ASSERT(mVk.GetSwapchainImagesKHR(mDevice, mSwapchain, &imageCount, mImages.data()) ==
440 | VK_SUCCESS);
441 |
442 | mImageViews.resize(imageCount, VK_NULL_HANDLE);
443 | mFramebuffers.resize(imageCount, VK_NULL_HANDLE);
444 |
445 | ALOGD("Successfully created swapchain");
446 | }
447 |
448 | static std::vector readFileFromAsset(AAssetManager* assetManager, const char* filePath,
449 | int mode) {
450 | ASSERT(filePath);
451 |
452 | AAsset* file = AAssetManager_open(assetManager, filePath, mode);
453 | ASSERT(file);
454 |
455 | auto fileLength = (size_t)AAsset_getLength(file);
456 | std::vector fileContent(fileLength);
457 | AAsset_read(file, fileContent.data(), fileLength);
458 | AAsset_close(file);
459 |
460 | return fileContent;
461 | }
462 |
463 | uint32_t Renderer::getMemoryTypeIndex(uint32_t typeBits, VkFlags mask) {
464 | VkPhysicalDeviceMemoryProperties memoryProperties;
465 | mVk.GetPhysicalDeviceMemoryProperties(mGpu, &memoryProperties);
466 |
467 | for (uint32_t typeIndex = 0; typeIndex < std::numeric_limits::digits; typeIndex++) {
468 | if ((typeBits & 1U) == 1 &&
469 | (memoryProperties.memoryTypes[typeIndex].propertyFlags & mask) == mask) {
470 | return typeIndex;
471 | }
472 | typeBits >>= 1U;
473 | }
474 | ASSERT(false);
475 | }
476 |
477 | void Renderer::setImageLayout(VkCommandBuffer commandBuffer, VkImage image,
478 | VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
479 | VkPipelineStageFlags srcStageMask,
480 | VkPipelineStageFlags dstStageMask) {
481 | VkImageMemoryBarrier imageMemoryBarrier = {
482 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
483 | .pNext = nullptr,
484 | .srcAccessMask = 0,
485 | .dstAccessMask = 0,
486 | .oldLayout = oldImageLayout,
487 | .newLayout = newImageLayout,
488 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
489 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
490 | .image = image,
491 | .subresourceRange =
492 | {
493 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
494 | .baseMipLevel = 0,
495 | .levelCount = 1,
496 | .baseArrayLayer = 0,
497 | .layerCount = 1,
498 | },
499 | };
500 |
501 | switch (oldImageLayout) {
502 | case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
503 | imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
504 | break;
505 |
506 | case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
507 | imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
508 | break;
509 |
510 | case VK_IMAGE_LAYOUT_PREINITIALIZED:
511 | imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
512 | break;
513 |
514 | default:
515 | break;
516 | }
517 |
518 | switch (newImageLayout) {
519 | case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
520 | imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
521 | break;
522 |
523 | case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
524 | imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
525 | break;
526 |
527 | case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
528 | imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
529 | break;
530 |
531 | case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
532 | imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
533 | break;
534 |
535 | case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
536 | imageMemoryBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
537 | break;
538 |
539 | default:
540 | break;
541 | }
542 |
543 | mVk.CmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1,
544 | &imageMemoryBarrier);
545 |
546 | ALOGD("Successfully transferred image layout from %u to %u", oldImageLayout, newImageLayout);
547 | }
548 |
549 | void Renderer::loadTextureFromFile(const char* filePath, Texture* outTexture) {
550 | std::vector file = readFileFromAsset(mAssetManager, filePath, AASSET_MODE_BUFFER);
551 | ASSERT(!file.empty());
552 |
553 | VkFormatProperties formatProperties;
554 | mVk.GetPhysicalDeviceFormatProperties(mGpu, VK_FORMAT_R8G8B8A8_UNORM, &formatProperties);
555 | ASSERT(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
556 |
557 | uint32_t imageWidth = 0;
558 | uint32_t imageHeight = 0;
559 | uint32_t channel = 0;
560 | uint8_t* imageData =
561 | stbi_load_from_memory((const stbi_uc*)file.data(), file.size(),
562 | reinterpret_cast(&imageWidth),
563 | reinterpret_cast(&imageHeight),
564 | reinterpret_cast(&channel), 4 /*desired_channels*/);
565 | ASSERT(imageData);
566 | ASSERT(imageWidth);
567 | ASSERT(imageHeight);
568 | ASSERT(channel == 4);
569 |
570 | // Create a stageImage and stageMemory for the original texture uploading
571 | VkImage stageImage = VK_NULL_HANDLE;
572 | VkDeviceMemory stageMemory = VK_NULL_HANDLE;
573 | VkImageCreateInfo imageCreateInfo = {
574 | .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
575 | .pNext = nullptr,
576 | .flags = 0,
577 | .imageType = VK_IMAGE_TYPE_2D,
578 | .format = VK_FORMAT_R8G8B8A8_UNORM,
579 | .extent =
580 | {
581 | .width = imageWidth,
582 | .height = imageHeight,
583 | .depth = 1,
584 | },
585 | .mipLevels = 1,
586 | .arrayLayers = 1,
587 | .samples = VK_SAMPLE_COUNT_1_BIT,
588 | .tiling = VK_IMAGE_TILING_LINEAR,
589 | .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
590 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
591 | .queueFamilyIndexCount = 1,
592 | .pQueueFamilyIndices = &mQueueFamilyIndex,
593 | .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
594 | };
595 | ASSERT(mVk.CreateImage(mDevice, &imageCreateInfo, nullptr, &stageImage) == VK_SUCCESS);
596 |
597 | VkMemoryRequirements memoryRequirements;
598 | mVk.GetImageMemoryRequirements(mDevice, stageImage, &memoryRequirements);
599 |
600 | const uint32_t typeIndex = getMemoryTypeIndex(memoryRequirements.memoryTypeBits,
601 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
602 | VkMemoryAllocateInfo memoryAllocateInfo = {
603 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
604 | .pNext = nullptr,
605 | .allocationSize = memoryRequirements.size,
606 | .memoryTypeIndex = typeIndex,
607 | };
608 | ASSERT(mVk.AllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &stageMemory) == VK_SUCCESS);
609 | ASSERT(mVk.BindImageMemory(mDevice, stageImage, stageMemory, 0) == VK_SUCCESS);
610 |
611 | const VkImageSubresource imageSubresource = {
612 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
613 | .mipLevel = 0,
614 | .arrayLayer = 0,
615 | };
616 | VkSubresourceLayout subresourceLayout;
617 | mVk.GetImageSubresourceLayout(mDevice, stageImage, &imageSubresource, &subresourceLayout);
618 |
619 | void* textureData;
620 | ASSERT(mVk.MapMemory(mDevice, stageMemory, 0, memoryAllocateInfo.allocationSize, 0,
621 | &textureData) == VK_SUCCESS);
622 |
623 | for (uint32_t row = 0, srcPos = 0, cols = 4 * imageWidth; row < imageHeight; row++) {
624 | for (uint32_t col = 0; col < cols; col++) {
625 | ((uint8_t*)textureData)[col] = imageData[srcPos++];
626 | }
627 | textureData = (uint8_t*)textureData + subresourceLayout.rowPitch;
628 | }
629 |
630 | mVk.UnmapMemory(mDevice, stageMemory);
631 | stbi_image_free(imageData);
632 | file.clear();
633 |
634 | // Create a tile texture to blit into
635 | imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
636 | imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
637 | imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
638 | ASSERT(mVk.CreateImage(mDevice, &imageCreateInfo, nullptr, &outTexture->image) == VK_SUCCESS);
639 |
640 | mVk.GetImageMemoryRequirements(mDevice, outTexture->image, &memoryRequirements);
641 |
642 | memoryAllocateInfo.allocationSize = memoryRequirements.size;
643 | memoryAllocateInfo.memoryTypeIndex = getMemoryTypeIndex(memoryRequirements.memoryTypeBits,
644 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
645 |
646 | ASSERT(mVk.AllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &outTexture->memory) ==
647 | VK_SUCCESS);
648 | ASSERT(mVk.BindImageMemory(mDevice, outTexture->image, outTexture->memory, 0) == VK_SUCCESS);
649 |
650 | VkCommandPoolCreateInfo commandPoolCreateInfo = {
651 | .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
652 | .pNext = nullptr,
653 | .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
654 | .queueFamilyIndex = mQueueFamilyIndex,
655 | };
656 | VkCommandPool commandPool;
657 | ASSERT(mVk.CreateCommandPool(mDevice, &commandPoolCreateInfo, nullptr, &commandPool) ==
658 | VK_SUCCESS);
659 |
660 | VkCommandBuffer commandBuffer;
661 | const VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
662 | .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
663 | .pNext = nullptr,
664 | .commandPool = commandPool,
665 | .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
666 | .commandBufferCount = 1,
667 | };
668 |
669 | ASSERT(mVk.AllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, &commandBuffer) ==
670 | VK_SUCCESS);
671 | VkCommandBufferBeginInfo commandBufferBeginInfo = {
672 | .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
673 | .pNext = nullptr,
674 | .flags = 0,
675 | .pInheritanceInfo = nullptr,
676 | };
677 | ASSERT(mVk.BeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) == VK_SUCCESS);
678 |
679 | setImageLayout(commandBuffer, stageImage, VK_IMAGE_LAYOUT_PREINITIALIZED,
680 | VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_HOST_BIT,
681 | VK_PIPELINE_STAGE_TRANSFER_BIT);
682 |
683 | // Transitions image out of UNDEFINED type
684 | setImageLayout(commandBuffer, outTexture->image, VK_IMAGE_LAYOUT_UNDEFINED,
685 | VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_HOST_BIT,
686 | VK_PIPELINE_STAGE_TRANSFER_BIT);
687 |
688 | const VkImageCopy blitInfo = {
689 | .srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
690 | .srcSubresource.mipLevel = 0,
691 | .srcSubresource.baseArrayLayer = 0,
692 | .srcSubresource.layerCount = 1,
693 | .srcOffset.x = 0,
694 | .srcOffset.y = 0,
695 | .srcOffset.z = 0,
696 | .dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
697 | .dstSubresource.mipLevel = 0,
698 | .dstSubresource.baseArrayLayer = 0,
699 | .dstSubresource.layerCount = 1,
700 | .dstOffset.x = 0,
701 | .dstOffset.y = 0,
702 | .dstOffset.z = 0,
703 | .extent.width = imageWidth,
704 | .extent.height = imageHeight,
705 | .extent.depth = 1,
706 | };
707 | mVk.CmdCopyImage(commandBuffer, stageImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
708 | outTexture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blitInfo);
709 |
710 | setImageLayout(commandBuffer, outTexture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
711 | VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
712 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
713 |
714 | ASSERT(mVk.EndCommandBuffer(commandBuffer) == VK_SUCCESS);
715 |
716 | const VkFenceCreateInfo fenceCreateInfo = {
717 | .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
718 | .pNext = nullptr,
719 | .flags = 0,
720 | };
721 | VkFence fence;
722 | ASSERT(mVk.CreateFence(mDevice, &fenceCreateInfo, nullptr, &fence) == VK_SUCCESS);
723 |
724 | const VkSubmitInfo submitInfo = {
725 | .pNext = nullptr,
726 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
727 | .waitSemaphoreCount = 0,
728 | .pWaitSemaphores = nullptr,
729 | .pWaitDstStageMask = nullptr,
730 | .commandBufferCount = 1,
731 | .pCommandBuffers = &commandBuffer,
732 | .signalSemaphoreCount = 0,
733 | .pSignalSemaphores = nullptr,
734 | };
735 | ASSERT(mVk.QueueSubmit(mQueue, 1, &submitInfo, fence) == VK_SUCCESS);
736 | ASSERT(mVk.WaitForFences(mDevice, 1, &fence, VK_TRUE, kTimeout30Sec) == VK_SUCCESS);
737 | mVk.DestroyFence(mDevice, fence, nullptr);
738 |
739 | mVk.FreeCommandBuffers(mDevice, commandPool, 1, &commandBuffer);
740 | mVk.DestroyCommandPool(mDevice, commandPool, nullptr);
741 | mVk.DestroyImage(mDevice, stageImage, nullptr);
742 | mVk.FreeMemory(mDevice, stageMemory, nullptr);
743 |
744 | // Record the image's original dimensions so we can respect it later
745 | outTexture->width = imageWidth;
746 | outTexture->height = imageHeight;
747 |
748 | ALOGD("Successfully loaded texture from %s", filePath);
749 | }
750 |
751 | void Renderer::createTextures() {
752 | mTextures.resize(kTextureCount);
753 | for (uint32_t i = 0; i < kTextureCount; i++) {
754 | loadTextureFromFile(kTextureFiles[i], &mTextures[i]);
755 |
756 | const VkSamplerCreateInfo samplerCreateInfo = {
757 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
758 | .pNext = nullptr,
759 | .flags = 0,
760 | .magFilter = VK_FILTER_NEAREST,
761 | .minFilter = VK_FILTER_NEAREST,
762 | .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
763 | .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
764 | .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
765 | .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
766 | .mipLodBias = 0.0F,
767 | .anisotropyEnable = VK_FALSE,
768 | .maxAnisotropy = 1,
769 | .compareEnable = VK_FALSE,
770 | .compareOp = VK_COMPARE_OP_NEVER,
771 | .minLod = 0.0F,
772 | .maxLod = 0.0F,
773 | .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
774 | .unnormalizedCoordinates = VK_FALSE,
775 | };
776 | ASSERT(mVk.CreateSampler(mDevice, &samplerCreateInfo, nullptr, &mTextures[i].sampler) ==
777 | VK_SUCCESS);
778 |
779 | const VkImageViewCreateInfo viewCreateInfo = {
780 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
781 | .pNext = nullptr,
782 | .flags = 0,
783 | .image = mTextures[i].image,
784 | .viewType = VK_IMAGE_VIEW_TYPE_2D,
785 | .format = VK_FORMAT_R8G8B8A8_UNORM,
786 | .components =
787 | {
788 | VK_COMPONENT_SWIZZLE_R,
789 | VK_COMPONENT_SWIZZLE_G,
790 | VK_COMPONENT_SWIZZLE_B,
791 | VK_COMPONENT_SWIZZLE_A,
792 | },
793 | .subresourceRange =
794 | {
795 | VK_IMAGE_ASPECT_COLOR_BIT,
796 | 0,
797 | 1,
798 | 0,
799 | 1,
800 | },
801 | };
802 | ASSERT(mVk.CreateImageView(mDevice, &viewCreateInfo, nullptr, &mTextures[i].view) ==
803 | VK_SUCCESS);
804 | }
805 |
806 | ALOGD("Successfully created textures");
807 | }
808 |
809 | void Renderer::createDescriptorSet() {
810 | const VkDescriptorSetLayoutBinding descriptorSetLayoutBinding = {
811 | .binding = 0,
812 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
813 | .descriptorCount = kTextureCount,
814 | .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
815 | .pImmutableSamplers = nullptr,
816 | };
817 | const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
818 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
819 | .pNext = nullptr,
820 | .bindingCount = 1,
821 | .pBindings = &descriptorSetLayoutBinding,
822 | };
823 | ASSERT(mVk.CreateDescriptorSetLayout(mDevice, &descriptorSetLayoutCreateInfo, nullptr,
824 | &mDescriptorSetLayout) == VK_SUCCESS);
825 |
826 | const VkDescriptorPoolSize descriptorPoolSize = {
827 | .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
828 | .descriptorCount = kTextureCount,
829 | };
830 | const VkDescriptorPoolCreateInfo descriptor_pool = {
831 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
832 | .pNext = nullptr,
833 | .flags = 0,
834 | .maxSets = 1,
835 | .poolSizeCount = 1,
836 | .pPoolSizes = &descriptorPoolSize,
837 | };
838 |
839 | ASSERT(mVk.CreateDescriptorPool(mDevice, &descriptor_pool, nullptr, &mDescriptorPool) ==
840 | VK_SUCCESS);
841 |
842 | VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
843 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
844 | .pNext = nullptr,
845 | .descriptorPool = mDescriptorPool,
846 | .descriptorSetCount = 1,
847 | .pSetLayouts = &mDescriptorSetLayout,
848 | };
849 | ASSERT(mVk.AllocateDescriptorSets(mDevice, &descriptorSetAllocateInfo, &mDescriptorSet) ==
850 | VK_SUCCESS);
851 |
852 | VkDescriptorImageInfo descriptorImageInfo[kTextureCount];
853 | for (uint32_t i = 0; i < kTextureCount; i++) {
854 | descriptorImageInfo[i].sampler = mTextures[i].sampler;
855 | descriptorImageInfo[i].imageView = mTextures[i].view;
856 | descriptorImageInfo[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
857 | }
858 |
859 | VkWriteDescriptorSet writeDescriptorSet = {
860 | .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
861 | .pNext = nullptr,
862 | .dstSet = mDescriptorSet,
863 | .dstBinding = 0,
864 | .dstArrayElement = 0,
865 | .descriptorCount = kTextureCount,
866 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
867 | .pImageInfo = descriptorImageInfo,
868 | .pBufferInfo = nullptr,
869 | .pTexelBufferView = nullptr,
870 | };
871 | mVk.UpdateDescriptorSets(mDevice, 1, &writeDescriptorSet, 0, nullptr);
872 |
873 | ALOGD("Successfully created descriptor set");
874 | }
875 |
876 | void Renderer::loadShaderFromFile(const char* filePath, VkShaderModule* outShader) {
877 | ASSERT(filePath);
878 |
879 | std::vector file = readFileFromAsset(mAssetManager, filePath, AASSET_MODE_BUFFER);
880 |
881 | const VkShaderModuleCreateInfo shaderModuleCreateInfo = {
882 | .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
883 | .pNext = nullptr,
884 | .flags = 0,
885 | .codeSize = file.size(),
886 | .pCode = (const uint32_t*)(file.data()),
887 | };
888 | ASSERT(mVk.CreateShaderModule(mDevice, &shaderModuleCreateInfo, nullptr, outShader) ==
889 | VK_SUCCESS);
890 |
891 | ALOGD("Successfully created shader module from %s", filePath);
892 | }
893 |
894 | void Renderer::createRenderPass() {
895 | const VkAttachmentDescription attachmentDescription = {
896 | .flags = 0,
897 | .format = mFormat,
898 | .samples = VK_SAMPLE_COUNT_1_BIT,
899 | .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
900 | .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
901 | .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
902 | .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
903 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
904 | .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
905 | };
906 | const VkAttachmentReference attachmentReference = {
907 | .attachment = 0,
908 | .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
909 | };
910 | const VkSubpassDescription subpassDescription = {
911 | .flags = 0,
912 | .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
913 | .inputAttachmentCount = 0,
914 | .pInputAttachments = nullptr,
915 | .colorAttachmentCount = 1,
916 | .pColorAttachments = &attachmentReference,
917 | .pResolveAttachments = nullptr,
918 | .pDepthStencilAttachment = nullptr,
919 | .preserveAttachmentCount = 0,
920 | .pPreserveAttachments = nullptr,
921 | };
922 | const VkRenderPassCreateInfo renderPassCreateInfo = {
923 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
924 | .pNext = nullptr,
925 | .flags = 0,
926 | .attachmentCount = 1,
927 | .pAttachments = &attachmentDescription,
928 | .subpassCount = 1,
929 | .pSubpasses = &subpassDescription,
930 | .dependencyCount = 0,
931 | .pDependencies = nullptr,
932 | };
933 | ASSERT(mVk.CreateRenderPass(mDevice, &renderPassCreateInfo, nullptr, &mRenderPass) ==
934 | VK_SUCCESS);
935 |
936 | ALOGD("Successfully created render pass");
937 | }
938 |
939 | void Renderer::createGraphicsPipeline() {
940 | const VkPushConstantRange pushConstantRange = {
941 | .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
942 | .offset = 0,
943 | .size = sizeof(PushConstantBlock),
944 | };
945 | const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
946 | .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
947 | .pNext = nullptr,
948 | .flags = 0,
949 | .setLayoutCount = 1,
950 | .pSetLayouts = &mDescriptorSetLayout,
951 | .pushConstantRangeCount = 1,
952 | .pPushConstantRanges = &pushConstantRange,
953 | };
954 | ASSERT(mVk.CreatePipelineLayout(mDevice, &pipelineLayoutCreateInfo, nullptr,
955 | &mPipelineLayout) == VK_SUCCESS);
956 |
957 | VkShaderModule vertexShader = VK_NULL_HANDLE;
958 | VkShaderModule fragmentShader = VK_NULL_HANDLE;
959 | loadShaderFromFile(kVertexShaderFile, &vertexShader);
960 | loadShaderFromFile(kFragmentShaderFile, &fragmentShader);
961 |
962 | const VkPipelineShaderStageCreateInfo shaderStages[2] = {
963 | {
964 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
965 | .pNext = nullptr,
966 | .flags = 0,
967 | .stage = VK_SHADER_STAGE_VERTEX_BIT,
968 | .module = vertexShader,
969 | .pName = "main",
970 | .pSpecializationInfo = nullptr,
971 | },
972 | {
973 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
974 | .pNext = nullptr,
975 | .flags = 0,
976 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
977 | .module = fragmentShader,
978 | .pName = "main",
979 | .pSpecializationInfo = nullptr,
980 | },
981 | };
982 | const VkVertexInputBindingDescription vertexInputBindingDescription = {
983 | .binding = 0,
984 | .stride = 4 * sizeof(float),
985 | .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
986 | };
987 | const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[2] = {
988 | {
989 | .location = 0,
990 | .binding = 0,
991 | .format = VK_FORMAT_R32G32_SFLOAT,
992 | .offset = 0,
993 | },
994 | {
995 |
996 | .location = 1,
997 | .binding = 0,
998 | .format = VK_FORMAT_R32G32_SFLOAT,
999 | .offset = sizeof(float) * 2,
1000 | },
1001 | };
1002 | const VkPipelineVertexInputStateCreateInfo vertexInputInfo = {
1003 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
1004 | .pNext = nullptr,
1005 | .flags = 0,
1006 | .vertexBindingDescriptionCount = 1,
1007 | .pVertexBindingDescriptions = &vertexInputBindingDescription,
1008 | .vertexAttributeDescriptionCount = 2,
1009 | .pVertexAttributeDescriptions = vertexInputAttributeDescriptions,
1010 | };
1011 | const VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo = {
1012 | .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
1013 | .pNext = nullptr,
1014 | .flags = 0,
1015 | .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
1016 | .primitiveRestartEnable = VK_FALSE,
1017 | };
1018 | const VkPipelineViewportStateCreateInfo viewportInfo = {
1019 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
1020 | .pNext = nullptr,
1021 | .flags = 0,
1022 | .viewportCount = 1,
1023 | .pViewports = nullptr,
1024 | .scissorCount = 1,
1025 | .pScissors = nullptr,
1026 | };
1027 | const VkPipelineRasterizationStateCreateInfo rasterInfo = {
1028 | .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
1029 | .pNext = nullptr,
1030 | .flags = 0,
1031 | .depthClampEnable = VK_FALSE,
1032 | .rasterizerDiscardEnable = VK_FALSE,
1033 | .polygonMode = VK_POLYGON_MODE_FILL,
1034 | .cullMode = VK_CULL_MODE_NONE,
1035 | .frontFace = VK_FRONT_FACE_CLOCKWISE,
1036 | .depthBiasEnable = VK_FALSE,
1037 | .depthBiasConstantFactor = 0,
1038 | .depthBiasClamp = 0,
1039 | .depthBiasSlopeFactor = 0,
1040 | .lineWidth = 1,
1041 | };
1042 | const VkSampleMask sampleMask = ~0U;
1043 | const VkPipelineMultisampleStateCreateInfo multisampleInfo = {
1044 | .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
1045 | .pNext = nullptr,
1046 | .flags = 0,
1047 | .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
1048 | .sampleShadingEnable = VK_FALSE,
1049 | .minSampleShading = 0,
1050 | .pSampleMask = &sampleMask,
1051 | .alphaToCoverageEnable = VK_FALSE,
1052 | .alphaToOneEnable = VK_FALSE,
1053 | };
1054 | const VkPipelineColorBlendAttachmentState attachmentStates = {
1055 | .blendEnable = VK_FALSE,
1056 | .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
1057 | .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
1058 | .colorBlendOp = VK_BLEND_OP_ADD,
1059 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
1060 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
1061 | .alphaBlendOp = VK_BLEND_OP_ADD,
1062 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
1063 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
1064 | };
1065 | const VkPipelineColorBlendStateCreateInfo colorBlendInfo = {
1066 | .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
1067 | .pNext = nullptr,
1068 | .flags = 0,
1069 | .logicOpEnable = VK_FALSE,
1070 | .logicOp = VK_LOGIC_OP_COPY,
1071 | .attachmentCount = 1,
1072 | .pAttachments = &attachmentStates,
1073 | .blendConstants = {0.0F, 0.0F, 0.0F, 0.0F},
1074 | };
1075 | const VkDynamicState dynamicStates[2] = {
1076 | VK_DYNAMIC_STATE_VIEWPORT,
1077 | VK_DYNAMIC_STATE_SCISSOR,
1078 | };
1079 | const VkPipelineDynamicStateCreateInfo dynamicInfo = {
1080 | .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
1081 | .pNext = nullptr,
1082 | .flags = 0,
1083 | .dynamicStateCount = 2,
1084 | .pDynamicStates = dynamicStates,
1085 | };
1086 | const VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
1087 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
1088 | .pNext = nullptr,
1089 | .flags = 0,
1090 | .stageCount = 2,
1091 | .pStages = shaderStages,
1092 | .pVertexInputState = &vertexInputInfo,
1093 | .pInputAssemblyState = &inputAssemblyInfo,
1094 | .pTessellationState = nullptr,
1095 | .pViewportState = &viewportInfo,
1096 | .pRasterizationState = &rasterInfo,
1097 | .pMultisampleState = &multisampleInfo,
1098 | .pDepthStencilState = nullptr,
1099 | .pColorBlendState = &colorBlendInfo,
1100 | .pDynamicState = &dynamicInfo,
1101 | .layout = mPipelineLayout,
1102 | .renderPass = mRenderPass,
1103 | .subpass = 0,
1104 | .basePipelineHandle = VK_NULL_HANDLE,
1105 | .basePipelineIndex = 0,
1106 | };
1107 | ASSERT(mVk.CreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr,
1108 | &mPipeline) == VK_SUCCESS);
1109 |
1110 | mVk.DestroyShaderModule(mDevice, vertexShader, nullptr);
1111 | mVk.DestroyShaderModule(mDevice, fragmentShader, nullptr);
1112 |
1113 | ALOGD("Successfully created graphics pipeline");
1114 | }
1115 |
1116 | void Renderer::createVertexBuffer() {
1117 | const float vertexData[16] = {
1118 | -1.0F, -1.0F, 0.0F, 0.0F, // LT
1119 | -1.0F, 1.0F, 0.0F, 1.0F, // LB
1120 | 1.0F, -1.0F, 1.0F, 0.0F, // RT
1121 | 1.0F, 1.0F, 1.0F, 1.0F, // RB
1122 | };
1123 |
1124 | const uint32_t queueFamilyIndex = mQueueFamilyIndex;
1125 | const VkBufferCreateInfo bufferCreateInfo = {
1126 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
1127 | .pNext = nullptr,
1128 | .flags = 0,
1129 | .size = sizeof(vertexData),
1130 | .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
1131 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
1132 | .queueFamilyIndexCount = 1,
1133 | .pQueueFamilyIndices = &queueFamilyIndex,
1134 | };
1135 | ASSERT(mVk.CreateBuffer(mDevice, &bufferCreateInfo, nullptr, &mVertexBuffer) == VK_SUCCESS);
1136 |
1137 | VkMemoryRequirements memoryRequirements;
1138 | mVk.GetBufferMemoryRequirements(mDevice, mVertexBuffer, &memoryRequirements);
1139 |
1140 | uint32_t typeIndex = getMemoryTypeIndex(memoryRequirements.memoryTypeBits,
1141 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
1142 | VkMemoryAllocateInfo memoryAllocateInfo = {
1143 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
1144 | .pNext = nullptr,
1145 | .allocationSize = memoryRequirements.size,
1146 | .memoryTypeIndex = typeIndex,
1147 | };
1148 | ASSERT(mVk.AllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &mVertexMemory) == VK_SUCCESS);
1149 |
1150 | void* data;
1151 | ASSERT(mVk.MapMemory(mDevice, mVertexMemory, 0, sizeof(vertexData), 0, &data) == VK_SUCCESS);
1152 |
1153 | memcpy(data, vertexData, sizeof(vertexData));
1154 | mVk.UnmapMemory(mDevice, mVertexMemory);
1155 |
1156 | ASSERT(mVk.BindBufferMemory(mDevice, mVertexBuffer, mVertexMemory, 0) == VK_SUCCESS);
1157 |
1158 | ALOGD("Successfully created vertex buffer");
1159 | }
1160 |
1161 | void Renderer::createCommandBuffers() {
1162 | const VkCommandPoolCreateInfo commandPoolCreateInfo = {
1163 | .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
1164 | .pNext = nullptr,
1165 | .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
1166 | .queueFamilyIndex = mQueueFamilyIndex,
1167 | };
1168 | ASSERT(mVk.CreateCommandPool(mDevice, &commandPoolCreateInfo, nullptr, &mCommandPool) ==
1169 | VK_SUCCESS);
1170 |
1171 | mCommandBuffers.resize(kInflight, VK_NULL_HANDLE);
1172 | const VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
1173 | .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
1174 | .pNext = nullptr,
1175 | .commandPool = mCommandPool,
1176 | .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
1177 | .commandBufferCount = kInflight,
1178 | };
1179 | ASSERT(mVk.AllocateCommandBuffers(mDevice, &commandBufferAllocateInfo,
1180 | mCommandBuffers.data()) == VK_SUCCESS);
1181 |
1182 | ALOGD("Successfully created command buffers");
1183 | }
1184 |
1185 | void Renderer::createSemaphore(VkSemaphore* outSemaphore) {
1186 | const VkSemaphoreCreateInfo semaphoreCreateInfo = {
1187 | .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
1188 | .pNext = nullptr,
1189 | .flags = 0,
1190 | };
1191 | ASSERT(mVk.CreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, outSemaphore) == VK_SUCCESS);
1192 | }
1193 |
1194 | void Renderer::createSemaphores() {
1195 | mAcquireSemaphores.resize(kInflight, VK_NULL_HANDLE);
1196 | mRenderSemaphores.resize(kInflight, VK_NULL_HANDLE);
1197 | for (int i = 0; i < kInflight; i++) {
1198 | createSemaphore(&mAcquireSemaphores[i]);
1199 | createSemaphore(&mRenderSemaphores[i]);
1200 | }
1201 |
1202 | ALOGD("Successfully created semaphores");
1203 | }
1204 |
1205 | void Renderer::createFences() {
1206 | mInflightFences.resize(kInflight, VK_NULL_HANDLE);
1207 | const VkFenceCreateInfo fenceCreateInfo = {
1208 | .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
1209 | .pNext = nullptr,
1210 | .flags = VK_FENCE_CREATE_SIGNALED_BIT,
1211 | };
1212 | for (int i = 0; i < kInflight; i++) {
1213 | ASSERT(mVk.CreateFence(mDevice, &fenceCreateInfo, nullptr, &mInflightFences[i]) ==
1214 | VK_SUCCESS);
1215 | }
1216 |
1217 | ALOGD("Successfully created fences");
1218 | }
1219 |
1220 | void Renderer::createFramebuffer(uint32_t index) {
1221 | const VkImageViewCreateInfo imageViewCreateInfo = {
1222 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1223 | .pNext = nullptr,
1224 | .flags = 0,
1225 | .image = mImages[index],
1226 | .viewType = VK_IMAGE_VIEW_TYPE_2D,
1227 | .format = mFormat,
1228 | .components =
1229 | {
1230 | .r = VK_COMPONENT_SWIZZLE_R,
1231 | .g = VK_COMPONENT_SWIZZLE_G,
1232 | .b = VK_COMPONENT_SWIZZLE_B,
1233 | .a = VK_COMPONENT_SWIZZLE_A,
1234 | },
1235 | .subresourceRange =
1236 | {
1237 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1238 | .baseMipLevel = 0,
1239 | .levelCount = 1,
1240 | .baseArrayLayer = 0,
1241 | .layerCount = 1,
1242 | },
1243 | };
1244 | ASSERT(mVk.CreateImageView(mDevice, &imageViewCreateInfo, nullptr, &mImageViews[index]) ==
1245 | VK_SUCCESS);
1246 |
1247 | const VkFramebufferCreateInfo framebufferCreateInfo = {
1248 | .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
1249 | .pNext = nullptr,
1250 | .flags = 0,
1251 | .renderPass = mRenderPass,
1252 | .attachmentCount = 1,
1253 | .pAttachments = &mImageViews[index],
1254 | .width = mImageWidth,
1255 | .height = mImageHeight,
1256 | .layers = 1,
1257 | };
1258 | ASSERT(mVk.CreateFramebuffer(mDevice, &framebufferCreateInfo, nullptr, &mFramebuffers[index]) ==
1259 | VK_SUCCESS);
1260 |
1261 | ALOGD("Successfully created framebuffer[%u]", index);
1262 | }
1263 |
1264 | void Renderer::recordCommandBuffer(uint32_t frameIndex, uint32_t imageIndex) {
1265 | const VkCommandBufferBeginInfo commandBufferBeginInfo = {
1266 | .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
1267 | .pNext = nullptr,
1268 | .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
1269 | .pInheritanceInfo = nullptr,
1270 | };
1271 | ASSERT(mVk.BeginCommandBuffer(mCommandBuffers[frameIndex], &commandBufferBeginInfo) ==
1272 | VK_SUCCESS);
1273 |
1274 | const VkClearValue clearVals = {
1275 | .color.float32[0] = 0.5F,
1276 | .color.float32[1] = 0.5F,
1277 | .color.float32[2] = 0.5F,
1278 | .color.float32[3] = 1.0F,
1279 | };
1280 | const VkRenderPassBeginInfo renderPassBeginInfo = {
1281 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
1282 | .pNext = nullptr,
1283 | .renderPass = mRenderPass,
1284 | .framebuffer = mFramebuffers[imageIndex],
1285 | .renderArea =
1286 | {
1287 | .offset =
1288 | {
1289 | .x = 0,
1290 | .y = 0,
1291 | },
1292 | .extent =
1293 | {
1294 | .width = mImageWidth,
1295 | .height = mImageHeight,
1296 | },
1297 | },
1298 | .clearValueCount = 1,
1299 | .pClearValues = &clearVals,
1300 | };
1301 | mVk.CmdBeginRenderPass(mCommandBuffers[frameIndex], &renderPassBeginInfo,
1302 | VK_SUBPASS_CONTENTS_INLINE);
1303 |
1304 | const VkViewport viewport = {
1305 | .x = 0.0F,
1306 | .y = 0.0F,
1307 | .width = (float)mImageWidth,
1308 | .height = (float)mImageHeight,
1309 | .minDepth = 0.0F,
1310 | .maxDepth = 1.0F,
1311 | };
1312 | mVk.CmdSetViewport(mCommandBuffers[frameIndex], 0, 1, &viewport);
1313 |
1314 | const VkRect2D scissor = {
1315 | .offset =
1316 | {
1317 | .x = 0,
1318 | .y = 0,
1319 | },
1320 | .extent =
1321 | {
1322 | .width = mImageWidth,
1323 | .height = mImageHeight,
1324 | },
1325 | };
1326 | mVk.CmdSetScissor(mCommandBuffers[frameIndex], 0, 1, &scissor);
1327 |
1328 | // Calculate the simple mvp for this demo
1329 | const float scaleW = mSurfaceWidth / (float)mTextures[0].width;
1330 | const float scaleH = mSurfaceHeight / (float)mTextures[0].height;
1331 | const float minimalScale = scaleW < scaleH ? scaleW : scaleH;
1332 | const float scaleX = minimalScale / scaleW;
1333 | const float scaleY = minimalScale / scaleH;
1334 | const glm::mat4 mvp = glm::scale(glm::mat4(1.0F), glm::vec3(scaleX, scaleY, 1.0F));
1335 |
1336 | // Generate the simple 2x2 rotation matrix for fixing pre-rotation in the clipping space
1337 | float radians = 0.0F;
1338 | switch (mPreTransform) {
1339 | case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
1340 | radians = glm::radians(90.0F);
1341 | break;
1342 | case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
1343 | radians = glm::radians(180.0F);
1344 | break;
1345 | case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
1346 | radians = glm::radians(270.0F);
1347 | break;
1348 | default:
1349 | break;
1350 | }
1351 | const glm::mat2 preRotate = glm::rotate(glm::mat4(1.0), radians, glm::vec3(0.0F, 0.0F, 1.0F));
1352 |
1353 | // We can do the preRotate * mvp here, but separate to be explicit in this demo
1354 | const PushConstantBlock pushConstantBlock = {
1355 | .mvp = mvp,
1356 | .preRotate = preRotate,
1357 | };
1358 | mVk.CmdPushConstants(mCommandBuffers[frameIndex], mPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT,
1359 | 0, sizeof(PushConstantBlock), &pushConstantBlock);
1360 |
1361 | mVk.CmdBindPipeline(mCommandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, mPipeline);
1362 |
1363 | mVk.CmdBindDescriptorSets(mCommandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS,
1364 | mPipelineLayout, 0, 1, &mDescriptorSet, 0, nullptr);
1365 |
1366 | const VkDeviceSize offset = 0;
1367 | mVk.CmdBindVertexBuffers(mCommandBuffers[frameIndex], 0, 1, &mVertexBuffer, &offset);
1368 |
1369 | mVk.CmdDraw(mCommandBuffers[frameIndex], 4, 1, 0, 0);
1370 |
1371 | mVk.CmdEndRenderPass(mCommandBuffers[frameIndex]);
1372 |
1373 | ASSERT(mVk.EndCommandBuffer(mCommandBuffers[frameIndex]) == VK_SUCCESS);
1374 | }
1375 |
1376 | void Renderer::destroyOldSwapchain() {
1377 | for (auto& framebuffer : mOldFramebuffers) {
1378 | mVk.DestroyFramebuffer(mDevice, framebuffer, nullptr);
1379 | }
1380 | mOldFramebuffers.clear();
1381 |
1382 | for (auto& imageView : mOldImageViews) {
1383 | mVk.DestroyImageView(mDevice, imageView, nullptr);
1384 | }
1385 | mOldImageViews.clear();
1386 |
1387 | mOldImages.clear();
1388 |
1389 | mVk.DestroySwapchainKHR(mDevice, mOldSwapchain, nullptr);
1390 | mOldSwapchain = VK_NULL_HANDLE;
1391 |
1392 | ALOGD("Successfully destroyed old swapchain");
1393 | }
1394 |
1395 | bool Renderer::is180Rotation() {
1396 | VkSurfaceCapabilitiesKHR surfaceCapabilities;
1397 | ASSERT(mVk.GetPhysicalDeviceSurfaceCapabilitiesKHR(mGpu, mSurface, &surfaceCapabilities) ==
1398 | VK_SUCCESS);
1399 | VkSurfaceTransformFlagsKHR currentTransform = surfaceCapabilities.currentTransform;
1400 | switch (currentTransform) {
1401 | case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
1402 | return mPreTransform == VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR;
1403 | case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
1404 | return mPreTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
1405 | case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
1406 | return mPreTransform == VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
1407 | case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
1408 | return mPreTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR;
1409 | default:
1410 | break;
1411 | }
1412 | return false;
1413 | }
1414 |
--------------------------------------------------------------------------------
/app/src/main/cpp/Renderer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 |
21 | #include
22 |
23 | #include "VkHelper.h"
24 |
25 | class Renderer {
26 | private:
27 | struct Texture {
28 | VkSampler sampler;
29 | VkImage image;
30 | VkDeviceMemory memory;
31 | VkImageView view;
32 | uint32_t width;
33 | uint32_t height;
34 |
35 | Texture()
36 | : sampler(VK_NULL_HANDLE),
37 | image(VK_NULL_HANDLE),
38 | memory(VK_NULL_HANDLE),
39 | view(VK_NULL_HANDLE),
40 | width(0),
41 | height(0) {}
42 | };
43 |
44 | public:
45 | explicit Renderer() {}
46 | void initialize(ANativeWindow* window, AAssetManager* assetManager);
47 | void drawFrame();
48 | void updateSurface(uint32_t width, uint32_t height);
49 | void destroy();
50 |
51 | private:
52 | void createInstance();
53 | void createDevice();
54 | void createSurface(ANativeWindow* window);
55 | void createSwapchain(VkSwapchainKHR oldSwapchain);
56 | uint32_t getMemoryTypeIndex(uint32_t typeBits, VkFlags mask);
57 | void setImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout oldImageLayout,
58 | VkImageLayout newImageLayout, VkPipelineStageFlags srcStages,
59 | VkPipelineStageFlags dstStages);
60 | void loadTextureFromFile(const char* filePath, Texture* outTexture);
61 | void createTextures();
62 | void createDescriptorSet();
63 | void createRenderPass();
64 | void loadShaderFromFile(const char* filePath, VkShaderModule* outShader);
65 | void createGraphicsPipeline();
66 | void createVertexBuffer();
67 | void createCommandBuffers();
68 | void createSemaphore(VkSemaphore* outSemaphore);
69 | void createSemaphores();
70 | void createFences();
71 | void createFramebuffer(uint32_t index);
72 | void recordCommandBuffer(uint32_t frameIndex, uint32_t imageIndex);
73 | void destroyOldSwapchain();
74 | bool is180Rotation();
75 |
76 | // Helper member for Vulkan entry points
77 | VkHelper mVk;
78 | // A pointer to cache AAssetManager
79 | AAssetManager* mAssetManager = nullptr;
80 |
81 | // Stable baseline members
82 | VkInstance mInstance = VK_NULL_HANDLE;
83 | VkPhysicalDevice mGpu = VK_NULL_HANDLE;
84 | VkDevice mDevice = VK_NULL_HANDLE;
85 | uint32_t mQueueFamilyIndex = 0;
86 | VkQueue mQueue = VK_NULL_HANDLE;
87 |
88 | // Swapchain related members
89 | VkSurfaceKHR mSurface = VK_NULL_HANDLE;
90 | VkFormat mFormat = VK_FORMAT_UNDEFINED;
91 | VkColorSpaceKHR mColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
92 | uint32_t mSurfaceWidth = 0;
93 | uint32_t mSurfaceHeight = 0;
94 | uint32_t mImageWidth = 0;
95 | uint32_t mImageHeight = 0;
96 | VkSurfaceTransformFlagBitsKHR mPreTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
97 | uint32_t mFrameCount = 0;
98 | VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
99 | std::vector mImages;
100 | std::vector mImageViews;
101 | std::vector mFramebuffers;
102 | // For swapchain recreation, old stuff can be refactored to kInflight buffered
103 | bool mFireRecreateSwapchain = false;
104 | uint32_t mPreRotationLatency = kPreRotationLatency;
105 | uint32_t mRetireFrame = 0;
106 | VkSwapchainKHR mOldSwapchain = VK_NULL_HANDLE;
107 | std::vector mOldImages;
108 | std::vector mOldImageViews;
109 | std::vector mOldFramebuffers;
110 |
111 | // Graphics pipeline related members
112 | VkRenderPass mRenderPass = VK_NULL_HANDLE;
113 | VkPipelineLayout mPipelineLayout = VK_NULL_HANDLE;
114 | VkPipeline mPipeline = VK_NULL_HANDLE;
115 |
116 | // Descriptor related members
117 | std::vector mTextures;
118 | VkDescriptorSetLayout mDescriptorSetLayout = VK_NULL_HANDLE;
119 | VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE;
120 | VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE;
121 |
122 | // Vertex buffer related members
123 | VkBuffer mVertexBuffer = VK_NULL_HANDLE;
124 | VkDeviceMemory mVertexMemory = VK_NULL_HANDLE;
125 |
126 | // Command buffer related members
127 | VkCommandPool mCommandPool = VK_NULL_HANDLE;
128 | std::vector mCommandBuffers;
129 |
130 | // Semaphores for synchronization
131 | std::vector mAcquireSemaphores;
132 | std::vector mRenderSemaphores;
133 |
134 | // Fences for latency control
135 | std::vector mInflightFences;
136 |
137 | // App specific constants
138 | static constexpr const char* kRequiredInstanceExtensions[2] = {
139 | "VK_KHR_surface",
140 | "VK_KHR_android_surface",
141 | };
142 | static constexpr const char* kRequiredDeviceExtensions[1] = {
143 | "VK_KHR_swapchain",
144 | };
145 | static constexpr const uint32_t kReqImageCount = 3;
146 | static constexpr const uint32_t kInflight = 2;
147 | static constexpr const uint32_t kTextureCount = 1;
148 | static constexpr const char* kTextureFiles[kTextureCount] = {
149 | "sample_tex.png",
150 | };
151 | static constexpr const char* kVertexShaderFile = "texture.vert.spv";
152 | static constexpr const char* kFragmentShaderFile = "texture.frag.spv";
153 | static constexpr const uint32_t kLogInterval = 100;
154 | static constexpr const uint64_t kTimeout30Sec = 30000000000;
155 | static constexpr const uint32_t kPreRotationLatency = 30;
156 | };
157 |
--------------------------------------------------------------------------------
/app/src/main/cpp/Utils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 |
21 | #define LOG_TAG "VKDEMO"
22 | #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
23 | #define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
24 | #define ASSERT(cond) \
25 | if (!(cond)) { \
26 | __android_log_assert(#cond, LOG_TAG, "Error: " #cond " at " __FILE__ ":%d", __LINE__); \
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/cpp/VkHelper.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include "VkHelper.h"
18 |
19 | #define GET_PROC(F) F = reinterpret_cast(vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F))
20 | #define GET_INST_PROC(F) F = reinterpret_cast(vkGetInstanceProcAddr(instance, "vk" #F))
21 | #define GET_DEV_PROC(F) F = reinterpret_cast(GetDeviceProcAddr(device, "vk" #F))
22 |
23 | void VkHelper::initializeGlobalApi() {
24 | GET_PROC(CreateInstance);
25 | GET_PROC(EnumerateInstanceExtensionProperties);
26 | GET_PROC(EnumerateInstanceVersion);
27 | }
28 |
29 | void VkHelper::initializeInstanceApi(VkInstance instance) {
30 | GET_INST_PROC(CreateAndroidSurfaceKHR);
31 | GET_INST_PROC(CreateDevice);
32 | GET_INST_PROC(DestroyInstance);
33 | GET_INST_PROC(DestroySurfaceKHR);
34 | GET_INST_PROC(EnumerateDeviceExtensionProperties);
35 | GET_INST_PROC(EnumeratePhysicalDevices);
36 | GET_INST_PROC(GetDeviceProcAddr);
37 | GET_INST_PROC(GetPhysicalDeviceMemoryProperties);
38 | GET_INST_PROC(GetPhysicalDeviceFormatProperties);
39 | GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
40 | GET_INST_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
41 | GET_INST_PROC(GetPhysicalDeviceSurfaceSupportKHR);
42 | GET_INST_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
43 | }
44 |
45 | void VkHelper::initializeDeviceApi(VkDevice device) {
46 | GET_DEV_PROC(AcquireNextImageKHR);
47 | GET_DEV_PROC(AllocateCommandBuffers);
48 | GET_DEV_PROC(AllocateDescriptorSets);
49 | GET_DEV_PROC(AllocateMemory);
50 | GET_DEV_PROC(BeginCommandBuffer);
51 | GET_DEV_PROC(BindBufferMemory);
52 | GET_DEV_PROC(BindImageMemory);
53 | GET_DEV_PROC(CmdBeginRenderPass);
54 | GET_DEV_PROC(CmdBindDescriptorSets);
55 | GET_DEV_PROC(CmdBindPipeline);
56 | GET_DEV_PROC(CmdBindVertexBuffers);
57 | GET_DEV_PROC(CmdCopyImage);
58 | GET_DEV_PROC(CmdDraw);
59 | GET_DEV_PROC(CmdEndRenderPass);
60 | GET_DEV_PROC(CmdPipelineBarrier);
61 | GET_DEV_PROC(CmdPushConstants);
62 | GET_DEV_PROC(CmdSetScissor);
63 | GET_DEV_PROC(CmdSetViewport);
64 | GET_DEV_PROC(CreateBuffer);
65 | GET_DEV_PROC(CreateCommandPool);
66 | GET_DEV_PROC(CreateDescriptorPool);
67 | GET_DEV_PROC(CreateDescriptorSetLayout);
68 | GET_DEV_PROC(CreateFence);
69 | GET_DEV_PROC(CreateFramebuffer);
70 | GET_DEV_PROC(CreateGraphicsPipelines);
71 | GET_DEV_PROC(CreateImage);
72 | GET_DEV_PROC(CreateImageView);
73 | GET_DEV_PROC(CreatePipelineLayout);
74 | GET_DEV_PROC(CreateRenderPass);
75 | GET_DEV_PROC(CreateSampler);
76 | GET_DEV_PROC(CreateSemaphore);
77 | GET_DEV_PROC(CreateShaderModule);
78 | GET_DEV_PROC(CreateSwapchainKHR);
79 | GET_DEV_PROC(DestroyBuffer);
80 | GET_DEV_PROC(DestroyCommandPool);
81 | GET_DEV_PROC(DestroyDescriptorPool);
82 | GET_DEV_PROC(DestroyDescriptorSetLayout);
83 | GET_DEV_PROC(DestroyDevice);
84 | GET_DEV_PROC(DestroyFence);
85 | GET_DEV_PROC(DestroyFramebuffer);
86 | GET_DEV_PROC(DestroyImage);
87 | GET_DEV_PROC(DestroyImageView);
88 | GET_DEV_PROC(DestroyPipeline);
89 | GET_DEV_PROC(DestroyPipelineLayout);
90 | GET_DEV_PROC(DestroyRenderPass);
91 | GET_DEV_PROC(DestroySampler);
92 | GET_DEV_PROC(DestroySemaphore);
93 | GET_DEV_PROC(DestroyShaderModule);
94 | GET_DEV_PROC(DestroySwapchainKHR);
95 | GET_DEV_PROC(DeviceWaitIdle);
96 | GET_DEV_PROC(EndCommandBuffer);
97 | GET_DEV_PROC(FreeCommandBuffers);
98 | GET_DEV_PROC(FreeMemory);
99 | GET_DEV_PROC(GetBufferMemoryRequirements);
100 | GET_DEV_PROC(GetDeviceQueue);
101 | GET_DEV_PROC(GetImageMemoryRequirements);
102 | GET_DEV_PROC(GetImageSubresourceLayout);
103 | GET_DEV_PROC(GetSwapchainImagesKHR);
104 | GET_DEV_PROC(MapMemory);
105 | GET_DEV_PROC(QueuePresentKHR);
106 | GET_DEV_PROC(QueueSubmit);
107 | GET_DEV_PROC(ResetFences);
108 | GET_DEV_PROC(UnmapMemory);
109 | GET_DEV_PROC(UpdateDescriptorSets);
110 | GET_DEV_PROC(WaitForFences);
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/cpp/VkHelper.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #pragma once
18 |
19 | #include
20 |
21 | class VkHelper {
22 | public:
23 | explicit VkHelper() {}
24 | void initializeGlobalApi();
25 | void initializeInstanceApi(VkInstance instance);
26 | void initializeDeviceApi(VkDevice device);
27 |
28 | // GET_PROC functions
29 | PFN_vkCreateInstance CreateInstance = nullptr;
30 | PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties = nullptr;
31 | PFN_vkEnumerateInstanceVersion EnumerateInstanceVersion = nullptr;
32 |
33 | // GET_INSTANCE_PROC functions
34 | PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR = nullptr;
35 | PFN_vkCreateDevice CreateDevice = nullptr;
36 | PFN_vkDestroyInstance DestroyInstance = nullptr;
37 | PFN_vkDestroySurfaceKHR DestroySurfaceKHR = nullptr;
38 | PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties = nullptr;
39 | PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices = nullptr;
40 | PFN_vkGetDeviceProcAddr GetDeviceProcAddr = nullptr;
41 | PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties = nullptr;
42 | PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties = nullptr;
43 | PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr;
44 | PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR = nullptr;
45 | PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR = nullptr;
46 | PFN_vkGetPhysicalDeviceQueueFamilyProperties GetPhysicalDeviceQueueFamilyProperties = nullptr;
47 |
48 | // GET_DEVICE_PROC functions
49 | PFN_vkAcquireNextImageKHR AcquireNextImageKHR = nullptr;
50 | PFN_vkAllocateCommandBuffers AllocateCommandBuffers = nullptr;
51 | PFN_vkAllocateDescriptorSets AllocateDescriptorSets = nullptr;
52 | PFN_vkAllocateMemory AllocateMemory = nullptr;
53 | PFN_vkBeginCommandBuffer BeginCommandBuffer = nullptr;
54 | PFN_vkBindBufferMemory BindBufferMemory = nullptr;
55 | PFN_vkBindImageMemory BindImageMemory = nullptr;
56 | PFN_vkCmdBeginRenderPass CmdBeginRenderPass = nullptr;
57 | PFN_vkCmdBindDescriptorSets CmdBindDescriptorSets = nullptr;
58 | PFN_vkCmdBindPipeline CmdBindPipeline = nullptr;
59 | PFN_vkCmdBindVertexBuffers CmdBindVertexBuffers = nullptr;
60 | PFN_vkCmdCopyImage CmdCopyImage = nullptr;
61 | PFN_vkCmdDraw CmdDraw = nullptr;
62 | PFN_vkCmdEndRenderPass CmdEndRenderPass = nullptr;
63 | PFN_vkCmdPipelineBarrier CmdPipelineBarrier = nullptr;
64 | PFN_vkCmdPushConstants CmdPushConstants = nullptr;
65 | PFN_vkCmdSetScissor CmdSetScissor = nullptr;
66 | PFN_vkCmdSetViewport CmdSetViewport = nullptr;
67 | PFN_vkCreateBuffer CreateBuffer = nullptr;
68 | PFN_vkCreateCommandPool CreateCommandPool = nullptr;
69 | PFN_vkCreateDescriptorPool CreateDescriptorPool = nullptr;
70 | PFN_vkCreateDescriptorSetLayout CreateDescriptorSetLayout = nullptr;
71 | PFN_vkCreateFence CreateFence = nullptr;
72 | PFN_vkCreateFramebuffer CreateFramebuffer = nullptr;
73 | PFN_vkCreateGraphicsPipelines CreateGraphicsPipelines = nullptr;
74 | PFN_vkCreateImage CreateImage = nullptr;
75 | PFN_vkCreateImageView CreateImageView = nullptr;
76 | PFN_vkCreatePipelineLayout CreatePipelineLayout = nullptr;
77 | PFN_vkCreateRenderPass CreateRenderPass = nullptr;
78 | PFN_vkCreateSampler CreateSampler = nullptr;
79 | PFN_vkCreateSemaphore CreateSemaphore = nullptr;
80 | PFN_vkCreateShaderModule CreateShaderModule = nullptr;
81 | PFN_vkCreateSwapchainKHR CreateSwapchainKHR = nullptr;
82 | PFN_vkDestroyBuffer DestroyBuffer = nullptr;
83 | PFN_vkDestroyCommandPool DestroyCommandPool = nullptr;
84 | PFN_vkDestroyDescriptorPool DestroyDescriptorPool = nullptr;
85 | PFN_vkDestroyDescriptorSetLayout DestroyDescriptorSetLayout = nullptr;
86 | PFN_vkDestroyDevice DestroyDevice = nullptr;
87 | PFN_vkDestroyFence DestroyFence = nullptr;
88 | PFN_vkDestroyFramebuffer DestroyFramebuffer = nullptr;
89 | PFN_vkDestroyImage DestroyImage = nullptr;
90 | PFN_vkDestroyImageView DestroyImageView = nullptr;
91 | PFN_vkDestroyPipeline DestroyPipeline = nullptr;
92 | PFN_vkDestroyPipelineLayout DestroyPipelineLayout = nullptr;
93 | PFN_vkDestroyRenderPass DestroyRenderPass = nullptr;
94 | PFN_vkDestroySampler DestroySampler = nullptr;
95 | PFN_vkDestroySemaphore DestroySemaphore = nullptr;
96 | PFN_vkDestroyShaderModule DestroyShaderModule = nullptr;
97 | PFN_vkDestroySwapchainKHR DestroySwapchainKHR = nullptr;
98 | PFN_vkDeviceWaitIdle DeviceWaitIdle = nullptr;
99 | PFN_vkEndCommandBuffer EndCommandBuffer = nullptr;
100 | PFN_vkFreeCommandBuffers FreeCommandBuffers = nullptr;
101 | PFN_vkFreeMemory FreeMemory = nullptr;
102 | PFN_vkGetBufferMemoryRequirements GetBufferMemoryRequirements = nullptr;
103 | PFN_vkGetDeviceQueue GetDeviceQueue = nullptr;
104 | PFN_vkGetImageMemoryRequirements GetImageMemoryRequirements = nullptr;
105 | PFN_vkGetImageSubresourceLayout GetImageSubresourceLayout = nullptr;
106 | PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR = nullptr;
107 | PFN_vkMapMemory MapMemory = nullptr;
108 | PFN_vkQueuePresentKHR QueuePresentKHR = nullptr;
109 | PFN_vkQueueSubmit QueueSubmit = nullptr;
110 | PFN_vkResetFences ResetFences = nullptr;
111 | PFN_vkUnmapMemory UnmapMemory = nullptr;
112 | PFN_vkUpdateDescriptorSets UpdateDescriptorSets = nullptr;
113 | PFN_vkWaitForFences WaitForFences = nullptr;
114 | };
115 |
--------------------------------------------------------------------------------
/app/src/main/cpp/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 | #include
19 |
20 | #include "Engine.h"
21 | #include "Utils.h"
22 |
23 | static void onChoreographer(int64_t frameTimeNanos, void* data) {
24 | if (!data) {
25 | return;
26 | }
27 |
28 | auto engine = static_cast(data);
29 | if (!engine->isReady()) {
30 | return;
31 | }
32 |
33 | AChoreographer_postFrameCallbackDelayed64(AChoreographer_getInstance(), onChoreographer, engine,
34 | engine->getDelayMillis(frameTimeNanos));
35 |
36 | engine->drawFrame();
37 | }
38 |
39 | static void handleAppCmd(android_app* app, int32_t cmd) {
40 | auto engine = static_cast(app->userData);
41 | switch (cmd) {
42 | case APP_CMD_INIT_WINDOW:
43 | engine->onInitWindow(app->window, app->activity->assetManager);
44 | AChoreographer_postFrameCallback64(AChoreographer_getInstance(), onChoreographer,
45 | engine);
46 | break;
47 | case APP_CMD_TERM_WINDOW:
48 | engine->onTermWindow();
49 | break;
50 | default:
51 | break;
52 | }
53 | }
54 |
55 | static void handleNativeWindowResized(ANativeActivity* activity, ANativeWindow* window) {
56 | auto engine = static_cast(static_cast(activity->instance)->userData);
57 | const int32_t width = ANativeWindow_getWidth(window);
58 | const int32_t height = ANativeWindow_getHeight(window);
59 | ALOGD("%s: W[%d], H[%d]", __FUNCTION__, width, height);
60 |
61 | if (width < 0 || height < 0) {
62 | return;
63 | }
64 | engine->onWindowResized((uint32_t)width, (uint32_t)height);
65 | }
66 |
67 | void android_main(android_app* app) {
68 | Engine engine;
69 |
70 | app->userData = &engine;
71 | app->onAppCmd = handleAppCmd;
72 | app->activity->callbacks->onNativeWindowResized = handleNativeWindowResized;
73 |
74 | if (AChoreographer_getInstance() == nullptr) {
75 | return;
76 | }
77 |
78 | while (true) {
79 | int events;
80 | android_poll_source* source;
81 |
82 | while (ALooper_pollAll(-1, nullptr, &events, (void**)&source) >= 0) {
83 | if (source != nullptr) {
84 | source->process(app, source);
85 | }
86 |
87 | if (app->destroyRequested != 0) {
88 | ALOGD("Destroy requested");
89 | engine.onTermWindow();
90 | return;
91 | }
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/vulkan-pre-rotation-demo/b3f836dee9d9925820e7631dc9f4b3fe77cfa56b/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/vulkan-pre-rotation-demo/b3f836dee9d9925820e7631dc9f4b3fe77cfa56b/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/vulkan-pre-rotation-demo/b3f836dee9d9925820e7631dc9f4b3fe77cfa56b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/vulkan-pre-rotation-demo/b3f836dee9d9925820e7631dc9f4b3fe77cfa56b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | vkdemo
3 |
4 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:4.0.0'
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/vulkan-pre-rotation-demo/b3f836dee9d9925820e7631dc9f4b3fe77cfa56b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Aug 17 23:33:32 PDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/third_party/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.4.1)
2 |
3 | # glm
4 | add_library(glm INTERFACE)
5 | target_sources(glm INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/glm/glm/glm.hpp)
6 | target_include_directories(glm INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/glm)
7 | target_compile_definitions(glm INTERFACE GLM_FORCE_SIZE_T_LENGTH GLM_FORCE_RADIANS)
8 |
9 | # stb
10 | add_library(stb INTERFACE)
11 | target_include_directories(stb INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/stb")
12 |
--------------------------------------------------------------------------------