From ed9d28afc8b18040cf90eb2db560f2878c40aad4 Mon Sep 17 00:00:00 2001 From: HanetakaChou Date: Thu, 16 Nov 2023 05:49:33 +0100 Subject: [PATCH] initial commit --- .github/workflows/build-android.yml | 25 + .github/workflows/build-linux.yml | 23 + .github/workflows/build-windows.yml | 23 + .gitmodules | 24 + LICENSE | 165 ++ README.md | 117 ++ README.png | Bin 0 -> 330518 bytes build-GLSL/GLSL.mk | 179 ++ build-android/.gitignore | 11 + build-android/APK.mk | 164 ++ build-android/Android.mk | 101 ++ build-android/AndroidManifest.xml | 17 + build-android/Application.mk | 24 + build-android/Demo-Android.sln | 64 + build-android/Demo-Android.vcxproj | 308 ++++ build-android/Demo-Android.vcxproj.filters | 160 ++ build-android/build-android.cmd | 29 + build-android/clean-android.cmd | 29 + build-android/libNativeActivity.map | 23 + build-android/rebuild-android.cmd | 31 + build-android/res/values/styles.xml | 7 + build-linux/.gitignore | 2 + build-linux/Demo-Linux.map | 21 + build-linux/Linux.mk | 233 +++ build-windows/.gitignore | 4 + build-windows/Demo-Windows-D3D12.vcxproj | 313 ++++ .../Demo-Windows-D3D12.vcxproj.filters | 220 +++ build-windows/Demo-Windows-VK.vcxproj | 395 +++++ build-windows/Demo-Windows-VK.vcxproj.filters | 243 +++ build-windows/Demo-Windows.sln | 51 + dxbc/.gitignore | 2 + dxbc/deferred_shading_fragment.inl | 7 + dxbc/deferred_shading_vertex.inl | 7 + dxbc/full_screen_transfer_fragment.inl | 7 + dxbc/full_screen_transfer_vertex.inl | 7 + dxbc/gbuffer_fragment.inl | 7 + dxbc/gbuffer_vertex.inl | 7 + dxbc/skin_compute.inl | 7 + shaders/common_constant.sli | 38 + shaders/common_resource_binding.sli | 36 + shaders/deferred_shading_fragment.sl | 180 ++ ...rred_shading_pipeline_resource_binding.sli | 35 + shaders/deferred_shading_vertex.sl | 33 + shaders/gbuffer_fragment.sl | 112 ++ shaders/gbuffer_pipeline_resource_binding.sli | 70 + shaders/gbuffer_vertex.sl | 72 + shaders/math_constant.sli | 23 + shaders/skin_compute.sl | 80 + shaders/skin_pipeline_resource_binding.sli | 45 + .../support/full_screen_transfer_fragment.sl | 45 + .../full_screen_transfer_pipeline_layout.sli | 32 + .../support/full_screen_transfer_vertex.sl | 33 + source/demo.cpp | 1449 +++++++++++++++++ source/demo.h | 128 ++ source/support/camera_controller.cpp | 201 +++ source/support/camera_controller.h | 54 + source/support/frame_throttling.h | 26 + source/support/main.cpp | 957 +++++++++++ source/support/renderer.cpp | 435 +++++ source/support/renderer.h | 31 + source/support/tick_count.cpp | 70 + source/support/tick_count.h | 28 + spirv/.gitignore | 2 + spirv/deferred_shading_fragment.inl | 7 + spirv/deferred_shading_vertex.inl | 7 + spirv/full_screen_transfer_fragment.inl | 7 + spirv/full_screen_transfer_vertex.inl | 7 + spirv/gbuffer_fragment.inl | 7 + spirv/gbuffer_vertex.inl | 7 + spirv/skin_compute.inl | 7 + thirdparty/Brioche | 1 + thirdparty/ConvertUTF | 1 + thirdparty/CoreRT | 1 + thirdparty/DLB | 1 + thirdparty/DirectXMath | 1 + thirdparty/ImportAsset | 1 + 76 files changed, 7327 insertions(+) create mode 100644 .github/workflows/build-android.yml create mode 100644 .github/workflows/build-linux.yml create mode 100644 .github/workflows/build-windows.yml create mode 100644 .gitmodules create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README.png create mode 100644 build-GLSL/GLSL.mk create mode 100644 build-android/.gitignore create mode 100644 build-android/APK.mk create mode 100644 build-android/Android.mk create mode 100644 build-android/AndroidManifest.xml create mode 100644 build-android/Application.mk create mode 100644 build-android/Demo-Android.sln create mode 100644 build-android/Demo-Android.vcxproj create mode 100644 build-android/Demo-Android.vcxproj.filters create mode 100644 build-android/build-android.cmd create mode 100644 build-android/clean-android.cmd create mode 100644 build-android/libNativeActivity.map create mode 100644 build-android/rebuild-android.cmd create mode 100644 build-android/res/values/styles.xml create mode 100644 build-linux/.gitignore create mode 100644 build-linux/Demo-Linux.map create mode 100644 build-linux/Linux.mk create mode 100644 build-windows/.gitignore create mode 100644 build-windows/Demo-Windows-D3D12.vcxproj create mode 100644 build-windows/Demo-Windows-D3D12.vcxproj.filters create mode 100644 build-windows/Demo-Windows-VK.vcxproj create mode 100644 build-windows/Demo-Windows-VK.vcxproj.filters create mode 100644 build-windows/Demo-Windows.sln create mode 100644 dxbc/.gitignore create mode 100644 dxbc/deferred_shading_fragment.inl create mode 100644 dxbc/deferred_shading_vertex.inl create mode 100644 dxbc/full_screen_transfer_fragment.inl create mode 100644 dxbc/full_screen_transfer_vertex.inl create mode 100644 dxbc/gbuffer_fragment.inl create mode 100644 dxbc/gbuffer_vertex.inl create mode 100644 dxbc/skin_compute.inl create mode 100644 shaders/common_constant.sli create mode 100644 shaders/common_resource_binding.sli create mode 100644 shaders/deferred_shading_fragment.sl create mode 100644 shaders/deferred_shading_pipeline_resource_binding.sli create mode 100644 shaders/deferred_shading_vertex.sl create mode 100644 shaders/gbuffer_fragment.sl create mode 100644 shaders/gbuffer_pipeline_resource_binding.sli create mode 100644 shaders/gbuffer_vertex.sl create mode 100644 shaders/math_constant.sli create mode 100644 shaders/skin_compute.sl create mode 100644 shaders/skin_pipeline_resource_binding.sli create mode 100644 shaders/support/full_screen_transfer_fragment.sl create mode 100644 shaders/support/full_screen_transfer_pipeline_layout.sli create mode 100644 shaders/support/full_screen_transfer_vertex.sl create mode 100644 source/demo.cpp create mode 100644 source/demo.h create mode 100644 source/support/camera_controller.cpp create mode 100644 source/support/camera_controller.h create mode 100644 source/support/frame_throttling.h create mode 100644 source/support/main.cpp create mode 100644 source/support/renderer.cpp create mode 100644 source/support/renderer.h create mode 100644 source/support/tick_count.cpp create mode 100644 source/support/tick_count.h create mode 100644 spirv/.gitignore create mode 100644 spirv/deferred_shading_fragment.inl create mode 100644 spirv/deferred_shading_vertex.inl create mode 100644 spirv/full_screen_transfer_fragment.inl create mode 100644 spirv/full_screen_transfer_vertex.inl create mode 100644 spirv/gbuffer_fragment.inl create mode 100644 spirv/gbuffer_vertex.inl create mode 100644 spirv/skin_compute.inl create mode 160000 thirdparty/Brioche create mode 160000 thirdparty/ConvertUTF create mode 160000 thirdparty/CoreRT create mode 160000 thirdparty/DLB create mode 160000 thirdparty/DirectXMath create mode 160000 thirdparty/ImportAsset diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml new file mode 100644 index 0000000..fe9d813 --- /dev/null +++ b/.github/workflows/build-android.yml @@ -0,0 +1,25 @@ +name: build android + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: windows-latest + strategy: + matrix: + use_debug_libraries: [true, false] + target_arch_abi: [armeabi-v7a, arm64-v8a, x86, x86_64] + steps: + - uses: actions/checkout@main + with: + submodules: 'recursive' + - shell: cmd + run: | + call "%ANDROID_NDK_ROOT%\ndk-build" -C "./thirdparty/Brioche/build-android" "NDK_PROJECT_PATH:=null" "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_${{matrix.use_debug_libraries}}\obj" "NDK_LIBS_OUT:=.\debug_${{matrix.use_debug_libraries}}\lib" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" + call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "./build-GLSL" -f ".\GLSL.mk" "APP_DEBUG:=${{matrix.use_debug_libraries}}" + call "%ANDROID_NDK_ROOT%\ndk-build" -C "./build-android" "NDK_PROJECT_PATH:=null" "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_${{matrix.use_debug_libraries}}\obj" "NDK_LIBS_OUT:=.\debug_${{matrix.use_debug_libraries}}\lib" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" + call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "./build-android" -f ".\APK.mk" "ANDROID_SDK_DIR:=%ANDROID_SDK_ROOT%\." "JDK_DIR:=%JAVA_HOME%\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml new file mode 100644 index 0000000..9df71b9 --- /dev/null +++ b/.github/workflows/build-linux.yml @@ -0,0 +1,23 @@ +name: build linux + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + use_debug_libraries: [true, false] + steps: + - uses: actions/checkout@main + with: + submodules: 'recursive' + - shell: bash + run: | + make -C "./thirdparty/Brioche/build-linux" -f "Linux.mk" "APP_DEBUG:=${{matrix.use_debug_libraries}}" + make -C "./build-GLSL" -f "GLSL.mk" "APP_DEBUG:=${{matrix.use_debug_libraries}}" + make -C "./build-linux" -f "Linux.mk" "APP_DEBUG:=${{matrix.use_debug_libraries}}" diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml new file mode 100644 index 0000000..02cc20c --- /dev/null +++ b/.github/workflows/build-windows.yml @@ -0,0 +1,23 @@ +name: build windows + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: windows-latest + strategy: + matrix: + configuration: [Debug, Release] + platform: [x86, x64] + steps: + - uses: actions/checkout@main + with: + submodules: 'recursive' + - uses: microsoft/setup-msbuild@main + - shell: cmd + run: | + msbuild ./build-windows/Demo-Windows.sln /p:Configuration=${{matrix.configuration}} /p:Platform=${{matrix.platform}} diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fdd8339 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,24 @@ +[submodule "thirdparty/Brioche"] + path = thirdparty/Brioche + url = git@github.com:HanetakaChou/Brioche.git + branch = master +[submodule "thirdparty/ConvertUTF"] + path = thirdparty/ConvertUTF + url = git@github.com:HanetakaChou/Import-Asset.git + branch = ConvertUTF +[submodule "thirdparty/CoreRT"] + path = thirdparty/CoreRT + url = git@github.com:HanetakaChou/Import-Asset.git + branch = CoreRT +[submodule "thirdparty/DirectXMath"] + path = thirdparty/DirectXMath + url = git@github.com:HanetakaChou/Import-Asset.git + branch = DirectXMath +[submodule "thirdparty/ImportAsset"] + path = thirdparty/ImportAsset + url = git@github.com:HanetakaChou/Import-Asset.git + branch = master +[submodule "thirdparty/DLB"] + path = thirdparty/DLB + url = git@github.com:HanetakaChou/Dual-Quaternion-Linear-Blending.git + branch = release diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5cc63c2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e7f9f16 --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ +## glTF Viewer + +[![build windows](https://github.com/HanetakaChou/glTF-Viewer/actions/workflows/build-windows.yml/badge.svg)](https://github.com/HanetakaChou/glTF-Viewer/actions/workflows/build-windows.yml) + +[![build linux](https://github.com/HanetakaChou/glTF-Viewer/actions/workflows/build-linux.yml/badge.svg)](https://github.com/HanetakaChou/glTF-Viewer/actions/workflows/build-linux.yml) + +[![build android](https://github.com/HanetakaChou/glTF-Viewer/actions/workflows/build-android.yml/badge.svg)](https://github.com/HanetakaChou/glTF-Viewer/actions/workflows/build-android.yml) + +![](README.png) + +```mermaid +graph LR + Demo_Scene["Demo Scene"] + Demo_Mesh["Demo Mesh"] + Demo_Mesh_Subset["Demo Mesh Subset"] + Demo_Mesh_Instance["Demo Mesh Instance"] + Demo_Mesh_Skinned_Subset["Demo Mesh Skinned Subset"] + Vertex_Position_Buffer["Vertex Position Buffer (e.g., Position)"] + Vertex_Varying_Buffer["Vertex Varying Buffer (e.g., Normal, Tangent, Texcoord)"] + Vertex_Joint_Buffer["Vertex Joint Buffer (e.g., Joint Indices, Joint Weights)"] + Index_Buffer["Index Buffer"] + Geometry_Material_Buffer["Geometry Material Buffer (e.g., Flags)"] + Material_Texture["Material Texture (e.g., Normal Texture, Emissive Texture, Base Color Texture, Material Roughness Texture)"] + GBuffer_Pipeline_Per_Mesh_Subset_Update_Descriptor_Set["

GBuffer Pipeline Per Mesh Subset Update Descriptor Set

Read Only Storage Buffer Vertex Position Buffer
Read Only Storage Buffer Vertex Varying Buffer
Read Only Storage Buffer Index Buffer
Read Only Storage Buffer Geometry Material Buffer
Sampled Image Material Textures
"] + Skin_Pipeline_Per_Mesh_Instance_Update_Uniform_Buffer["Skin Pipeline Per Mesh Instance Update Uniform Buffer (e.g., Joint Matrices)"] + GBuffer_Pipeline_Per_Mesh_Instance_Update_Uniform_Buffer["GBuffer Pipeline Per Mesh Instance Update Uniform Buffer (e.g., Model Transform)"] + Skin_Pipeline_Per_Mesh_Instance_Update_Descriptor_Set["

Skin Pipeline Per Mesh Instance Update Descriptor Set

Dynamic Uniform Buffer Skin Pipeline Per Mesh Instance Update Uniform Buffer
"] + GBuffer_Pipeline_Per_Mesh_Instance_Update_Descriptor_Set["

GBuffer Pipeline Per Mesh Instance Update Descriptor Set

Dynamic Uniform Buffer GBuffer Pipeline Per Mesh Instance Update Uniform Buffer
"] + Skinned_Vertex_Position_Buffer["Skinned Vertex Position Buffer"] + Skinned_Vertex_Varying_Buffer["Skinned Vertex Varying Buffer"] + Skin_Pipeline_Per_Mesh_Skinned_Subset_Update_Descriptor_Set["

Skin Pipeline Per Mesh Skinned Subset Update Descriptor Set

Read Only Storage Buffer Vertex Position Buffer
Read Only Storage Buffer Vertex Varying Buffer
Read Only Storage Buffer Vertex Joint Buffer
Write Only Storage Buffer Skinned Vertex Position Buffer
Write Only Storage Buffer Skinned Vertex Varying Buffer
"] + GBuffer_Pipeline_Per_Mesh_Skinned_Subset_Update_Descriptor_Set["

GBuffer Pipeline Per Mesh Skinned Subset Update Descriptor Set

Read Only Storage Buffer Skinned Vertex Position Buffer
Read Only Storage Buffer Skinned Vertex Varying Buffer
Read Only Storage Buffer Index Buffer
Read Only Storage Buffer Geometry Material Buffer
Sampled Image Material Textures
"] + + Demo_Scene ---> |"1 ... *"| Demo_Mesh + Demo_Mesh ---> |"1 ... *"| Demo_Mesh_Subset + Demo_Mesh ---> |"1 ... *"| Demo_Mesh_Instance + Demo_Mesh_Instance ---> |"1 ... *"| Demo_Mesh_Skinned_Subset + Demo_Mesh_Skinned_Subset <---> |"1 ... 1"| Demo_Mesh_Subset + Demo_Mesh_Subset ---> |"1 ... 1"| Vertex_Position_Buffer + Demo_Mesh_Subset ---> |"1 ... 1"| Vertex_Varying_Buffer + Demo_Mesh_Subset ---> |"1 ... 1"| Vertex_Joint_Buffer + Demo_Mesh_Subset ---> |"1 ... 1"| Index_Buffer + Demo_Mesh_Subset ---> |"1 ... 1"| Geometry_Material_Buffer + Demo_Mesh_Subset ---> |"1 ... *"| Material_Texture + Demo_Mesh_Subset ---> |"1 ... 1"| GBuffer_Pipeline_Per_Mesh_Subset_Update_Descriptor_Set + Demo_Mesh_Instance ---> |"1 ... 1"| Skin_Pipeline_Per_Mesh_Instance_Update_Uniform_Buffer + Demo_Mesh_Instance ---> |"1 ... 1"| GBuffer_Pipeline_Per_Mesh_Instance_Update_Uniform_Buffer + Demo_Mesh_Instance ---> |"1 ... 1"| Skin_Pipeline_Per_Mesh_Instance_Update_Descriptor_Set + Demo_Mesh_Instance ---> |"1 ... 1"| GBuffer_Pipeline_Per_Mesh_Instance_Update_Descriptor_Set + Demo_Mesh_Skinned_Subset ---> |"1 ... 1"| Skinned_Vertex_Position_Buffer + Demo_Mesh_Skinned_Subset ---> |"1 ... 1"| Skinned_Vertex_Varying_Buffer + Demo_Mesh_Skinned_Subset ---> |"1 ... 1"| Skin_Pipeline_Per_Mesh_Skinned_Subset_Update_Descriptor_Set + Demo_Mesh_Skinned_Subset ---> |"1 ... 1"| GBuffer_Pipeline_Per_Mesh_Skinned_Subset_Update_Descriptor_Set + + subgraph Demo_Mesh_Subset_Members["Demo Mesh Subset Members"] + Vertex_Position_Buffer + Vertex_Varying_Buffer + Vertex_Joint_Buffer + Index_Buffer + Geometry_Material_Buffer + Material_Texture + GBuffer_Pipeline_Per_Mesh_Subset_Update_Descriptor_Set + end + + subgraph Demo_Mesh_Instance_Members["Demo Mesh Instance Members"] + Skin_Pipeline_Per_Mesh_Instance_Update_Uniform_Buffer + GBuffer_Pipeline_Per_Mesh_Instance_Update_Uniform_Buffer + Skin_Pipeline_Per_Mesh_Instance_Update_Descriptor_Set + GBuffer_Pipeline_Per_Mesh_Instance_Update_Descriptor_Set + end + + subgraph Demo_Mesh_Skinned_Subset_Members["Demo Mesh Skinned Subset Members"] + Skinned_Vertex_Position_Buffer + Skinned_Vertex_Varying_Buffer + Skin_Pipeline_Per_Mesh_Skinned_Subset_Update_Descriptor_Set + GBuffer_Pipeline_Per_Mesh_Skinned_Subset_Update_Descriptor_Set + end +``` + +```mermaid +graph TD + CPU_Write_Upload["CPU Write Upload"] + Mesh_Instance_Skin_Pipeline_Upload_Buffer["Mesh Instance Skin Pipeline Upload Buffer"] + Mesh_Instance_GBuffer_Pipeline_Upload_Buffer["Mesh Instance GBuffer Pipeline Upload Buffer"] + Mesh_Subset_Asset_Vertex_Position_Varying_Buffer["Mesh Subset Asset Vertex Position Buffer


Mesh Subset Asset Vertex Varying Buffer"] + Mesh_Subset_Asset_Vertex_Joint_Buffer["Mesh Subset Asset Vertex Joint Buffer"] + Skin_Pass["
Skin Pass
"] + Mesh_Skinned_Subset_Intermediate_Skinned_Vertex_Position_Varying_Buffer["Mesh Skinned Subset Intermediate Skinned Vertex Position Buffer


Mesh Skinned Subset Intermediate Skinned Vertex Varying Buffer"] + Mesh_Subset_Asset_Index_Buffer["Mesh Subset Asset Index Buffer"] + Mesh_Subset_Asset_Geometry_Metrial_Buffer_Texture["Mesh Subset Asset Geometry Material Buffer


Mesh Subset Asset Material Textures"] + GBuffer_Pass["
GBuffer Pass
"] + GBuffer_Color_Attachment["GBuffer Color Attachment"] + GBuffer_Depth_Attachment["GBuffer Depth Attachment"] + Deferred_Shading_Pass["
Deferred Shading Pass
"] + Deferred_Shading_Color_Attachment["Deferred Shading Color Attachment" ] + Full_Screen_Transfer_Pass["Full Screen Transfer Pass"] + Swap_Chain_Image["Swap Chain Image"] + + CPU_Write_Upload --->|"Frame Throttling Index"| Mesh_Instance_Skin_Pipeline_Upload_Buffer + CPU_Write_Upload --->|"Frame Throttling Index"| Mesh_Instance_GBuffer_Pipeline_Upload_Buffer + Mesh_Instance_Skin_Pipeline_Upload_Buffer ---> Skin_Pass + Mesh_Subset_Asset_Vertex_Position_Varying_Buffer ---> Skin_Pass + Mesh_Subset_Asset_Vertex_Joint_Buffer ---> Skin_Pass + Skin_Pass --> Mesh_Skinned_Subset_Intermediate_Skinned_Vertex_Position_Varying_Buffer + Mesh_Instance_GBuffer_Pipeline_Upload_Buffer ---> GBuffer_Pass + Mesh_Subset_Asset_Vertex_Position_Varying_Buffer ---> GBuffer_Pass + Mesh_Skinned_Subset_Intermediate_Skinned_Vertex_Position_Varying_Buffer ---> GBuffer_Pass + Mesh_Subset_Asset_Index_Buffer ---> GBuffer_Pass + Mesh_Subset_Asset_Geometry_Metrial_Buffer_Texture ---> GBuffer_Pass + GBuffer_Pass ---> GBuffer_Color_Attachment + GBuffer_Pass ---> GBuffer_Depth_Attachment + GBuffer_Color_Attachment --> Deferred_Shading_Pass + GBuffer_Depth_Attachment --> Deferred_Shading_Pass + Deferred_Shading_Pass --> Deferred_Shading_Color_Attachment + Deferred_Shading_Color_Attachment --> Full_Screen_Transfer_Pass + Full_Screen_Transfer_Pass --> Swap_Chain_Image +``` diff --git a/README.png b/README.png new file mode 100644 index 0000000000000000000000000000000000000000..43e17927f6e621d29c9798d0c5f3b53b6085c4f0 GIT binary patch literal 330518 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�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 literal 0 HcmV?d00001 diff --git a/build-GLSL/GLSL.mk b/build-GLSL/GLSL.mk new file mode 100644 index 0000000..e871f7e --- /dev/null +++ b/build-GLSL/GLSL.mk @@ -0,0 +1,179 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ==================================================================== +# +# Host system auto-detection. +# +# ==================================================================== +ifeq ($(OS),Windows_NT) + # On all modern variants of Windows (including Cygwin and Wine) + # the OS environment variable is defined to 'Windows_NT' + # + # The value of PROCESSOR_ARCHITECTURE will be x86 or AMD64 + # + HOST_OS := windows + + # Trying to detect that we're running from Cygwin is tricky + # because we can't use $(OSTYPE): It's a Bash shell variable + # that is not exported to sub-processes, and isn't defined by + # other shells (for those with really weird setups). + # + # Instead, we assume that a program named /bin/uname.exe + # that can be invoked and returns a valid value corresponds + # to a Cygwin installation. + # + UNAME := $(shell /bin/uname.exe -s 2>NUL) + ifneq (,$(filter CYGWIN% MINGW32% MINGW64%,$(UNAME))) + HOST_OS := unix + _ := $(shell rm -f NUL) # Cleaning up + endif +else + HOST_OS := unix +endif + +# ----------------------------------------------------------------------------- +# Function : host-mkdir +# Arguments: 1: directory path +# Usage : $(call host-mkdir, +# Rationale: This function expands to the host-specific shell command used +# to create a path if it doesn't exist. +# ----------------------------------------------------------------------------- +ifeq ($(HOST_OS),windows) +host-mkdir = md $(subst /,\,"$1") >NUL 2>NUL || rem +else +host-mkdir = mkdir -p $1 +endif + +# ----------------------------------------------------------------------------- +# Function : host-rm +# Arguments: 1: list of files +# Usage : $(call host-rm,) +# Rationale: This function expands to the host-specific shell command used +# to remove some files. +# ----------------------------------------------------------------------------- +ifeq ($(HOST_OS),windows) +host-rm = \ + $(eval __host_rm_files := $(foreach __host_rm_file,$1,$(subst /,\,$(wildcard $(__host_rm_file)))))\ + $(if $(__host_rm_files),del /f/q $(__host_rm_files) >NUL 2>NUL || rem) +else +host-rm = rm -f $1 +endif + +# +# Copyright (C) YuqiaoZhang(HanetakaChou) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# + +HIDE := @ + +LOCAL_PATH := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) +SHADERS_DIR := $(LOCAL_PATH)/../shaders +ifeq (true, $(APP_DEBUG)) + SPIRV_DIR := $(LOCAL_PATH)/../spirv/debug +else + SPIRV_DIR := $(LOCAL_PATH)/../spirv/release +endif +THIRD_PARTY_DIR := $(LOCAL_PATH)/../thirdparty +ifeq ($(OS),Windows_NT) + GLSL_COMPILER_PATH := $(THIRD_PARTY_DIR)/Brioche/thirdparty/shaderc/bin/win32/x64/glslc.exe +else + GLSL_COMPILER_PATH := $(THIRD_PARTY_DIR)/Brioche/thirdparty/shaderc/bin/linux/x64/glslc +endif + +GLSL_COMPILER_FLAGS := +ifeq (true, $(APP_DEBUG)) + GLSL_COMPILER_FLAGS += -g -O0 +else + GLSL_COMPILER_FLAGS += -O +endif + +all : \ + $(SPIRV_DIR)/_internal_full_screen_transfer_vertex.inl \ + $(SPIRV_DIR)/_internal_full_screen_transfer_fragment.inl \ + $(SPIRV_DIR)/_internal_skin_compute.inl \ + $(SPIRV_DIR)/_internal_gbuffer_vertex.inl \ + $(SPIRV_DIR)/_internal_gbuffer_fragment.inl \ + $(SPIRV_DIR)/_internal_deferred_shading_vertex.inl \ + $(SPIRV_DIR)/_internal_deferred_shading_fragment.inl + +$(SPIRV_DIR)/_internal_full_screen_transfer_vertex.inl : $(SHADERS_DIR)/support/full_screen_transfer_vertex.sl + $(HIDE) $(call host-mkdir,$(SPIRV_DIR)) + $(HIDE) "$(GLSL_COMPILER_PATH)" -std=310es -mfmt=num -fshader-stage=vert $(GLSL_COMPILER_FLAGS) -MD -MF "$(SPIRV_DIR)/_internal_full_screen_transfer_vertex.d" -o "$(SPIRV_DIR)/_internal_full_screen_transfer_vertex.inl" "$(SHADERS_DIR)/support/full_screen_transfer_vertex.sl" + +$(SPIRV_DIR)/_internal_full_screen_transfer_fragment.inl : $(SHADERS_DIR)/support/full_screen_transfer_fragment.sl + $(HIDE) $(call host-mkdir,$(SPIRV_DIR)) + $(HIDE) "$(GLSL_COMPILER_PATH)" -std=310es -mfmt=num -fshader-stage=frag $(GLSL_COMPILER_FLAGS) -MD -MF "$(SPIRV_DIR)/_internal_full_screen_transfer_fragment.d" -o "$(SPIRV_DIR)/_internal_full_screen_transfer_fragment.inl" "$(SHADERS_DIR)/support/full_screen_transfer_fragment.sl" + +$(SPIRV_DIR)/_internal_skin_compute.inl : $(SHADERS_DIR)/skin_compute.sl + $(HIDE) $(call host-mkdir,$(SPIRV_DIR)) + $(HIDE) "$(GLSL_COMPILER_PATH)" -std=310es -mfmt=num -fshader-stage=comp $(GLSL_COMPILER_FLAGS) -MD -MF "$(SPIRV_DIR)/_internal_skin_compute.d" -o "$(SPIRV_DIR)/_internal_skin_compute.inl" "$(SHADERS_DIR)/skin_compute.sl" + +$(SPIRV_DIR)/_internal_gbuffer_vertex.inl : $(SHADERS_DIR)/gbuffer_vertex.sl + $(HIDE) $(call host-mkdir,$(SPIRV_DIR)) + $(HIDE) "$(GLSL_COMPILER_PATH)" -std=310es -mfmt=num -fshader-stage=vert $(GLSL_COMPILER_FLAGS) -MD -MF "$(SPIRV_DIR)/_internal_gbuffer_vertex.d" -o "$(SPIRV_DIR)/_internal_gbuffer_vertex.inl" "$(SHADERS_DIR)/gbuffer_vertex.sl" + +$(SPIRV_DIR)/_internal_gbuffer_fragment.inl : $(SHADERS_DIR)/gbuffer_fragment.sl + $(HIDE) $(call host-mkdir,$(SPIRV_DIR)) + $(HIDE) "$(GLSL_COMPILER_PATH)" -std=310es -mfmt=num -fshader-stage=frag $(GLSL_COMPILER_FLAGS) -MD -MF "$(SPIRV_DIR)/_internal_gbuffer_fragment.d" -o "$(SPIRV_DIR)/_internal_gbuffer_fragment.inl" "$(SHADERS_DIR)/gbuffer_fragment.sl" + +$(SPIRV_DIR)/_internal_deferred_shading_vertex.inl : $(SHADERS_DIR)/deferred_shading_vertex.sl + $(HIDE) $(call host-mkdir,$(SPIRV_DIR)) + $(HIDE) "$(GLSL_COMPILER_PATH)" -std=310es -mfmt=num -fshader-stage=vert $(GLSL_COMPILER_FLAGS) -MD -MF "$(SPIRV_DIR)/_internal_deferred_shading_vertex.d" -o "$(SPIRV_DIR)/_internal_deferred_shading_vertex.inl" "$(SHADERS_DIR)/deferred_shading_vertex.sl" + +$(SPIRV_DIR)/_internal_deferred_shading_fragment.inl : $(SHADERS_DIR)/deferred_shading_fragment.sl + $(HIDE) $(call host-mkdir,$(SPIRV_DIR)) + $(HIDE) "$(GLSL_COMPILER_PATH)" -std=310es -mfmt=num -fshader-stage=frag $(GLSL_COMPILER_FLAGS) -MD -MF "$(SPIRV_DIR)/_internal_deferred_shading_fragment.d" -o "$(SPIRV_DIR)/_internal_deferred_shading_fragment.inl" "$(SHADERS_DIR)/deferred_shading_fragment.sl" + +-include \ + $(SPIRV_DIR)/_internal_full_screen_transfer_vertex.d \ + $(SPIRV_DIR)/_internal_full_screen_transfer_fragment.d \ + $(SPIRV_DIR)/_internal_skin_compute.d \ + $(SPIRV_DIR)/_internal_gbuffer_vertex.d \ + $(SPIRV_DIR)/_internal_gbuffer_fragment.d \ + $(SPIRV_DIR)/_internal_deferred_shading_vertex.d \ + $(SPIRV_DIR)/_internal_deferred_shading_fragment.d + +clean: + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_full_screen_transfer_vertex.inl) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_full_screen_transfer_fragment.inl) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_skin_compute.inl) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_gbuffer_vertex.inl) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_gbuffer_fragment.inl) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_deferred_shading_vertex.inl) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_deferred_shading_fragment.inl) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_full_screen_transfer_vertex.d) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_full_screen_transfer_fragment.d) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_skin_compute.d) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_gbuffer_vertex.d) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_gbuffer_fragment.d) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_deferred_shading_vertex.d) + $(HIDE) $(call host-rm,$(SPIRV_DIR)/_internal_deferred_shading_fragment.d) + +.PHONY : \ + all \ + clean diff --git a/build-android/.gitignore b/build-android/.gitignore new file mode 100644 index 0000000..f922774 --- /dev/null +++ b/build-android/.gitignore @@ -0,0 +1,11 @@ +/debug_true +/debug_false +/keystore +/flat +/apk + +/.vs +/.agde +/bin-agde +/obj-agde +/*.vcxproj.user diff --git a/build-android/APK.mk b/build-android/APK.mk new file mode 100644 index 0000000..e9f8671 --- /dev/null +++ b/build-android/APK.mk @@ -0,0 +1,164 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ==================================================================== +# +# Host system auto-detection. +# +# ==================================================================== +ifeq ($(OS),Windows_NT) + # On all modern variants of Windows (including Cygwin and Wine) + # the OS environment variable is defined to 'Windows_NT' + # + # The value of PROCESSOR_ARCHITECTURE will be x86 or AMD64 + # + HOST_OS := windows + + # Trying to detect that we're running from Cygwin is tricky + # because we can't use $(OSTYPE): It's a Bash shell variable + # that is not exported to sub-processes, and isn't defined by + # other shells (for those with really weird setups). + # + # Instead, we assume that a program named /bin/uname.exe + # that can be invoked and returns a valid value corresponds + # to a Cygwin installation. + # + UNAME := $(shell /bin/uname.exe -s 2>NUL) + ifneq (,$(filter CYGWIN% MINGW32% MINGW64%,$(UNAME))) + HOST_OS := unix + _ := $(shell rm -f NUL) # Cleaning up + endif +else + HOST_OS := unix +endif + +# ----------------------------------------------------------------------------- +# Function : host-mkdir +# Arguments: 1: directory path +# Usage : $(call host-mkdir, +# Rationale: This function expands to the host-specific shell command used +# to create a path if it doesn't exist. +# ----------------------------------------------------------------------------- +ifeq ($(HOST_OS),windows) +host-mkdir = md $(subst /,\,"$1") >NUL 2>NUL || rem +else +host-mkdir = mkdir -p $1 +endif + +# ----------------------------------------------------------------------------- +# Function : host-cp +# Arguments: 1: source file +# 2: target file +# Usage : $(call host-cp,,) +# Rationale: This function expands to the host-specific shell command used +# to copy a single file +# ----------------------------------------------------------------------------- +ifeq ($(HOST_OS),windows) +host-cp = copy /b/y $(subst /,\,"$1" "$2") > NUL 2>NUL || rem +else +host-cp = cp -f $1 $2 +endif + +# ----------------------------------------------------------------------------- +# Function : host-rm +# Arguments: 1: list of files +# Usage : $(call host-rm,) +# Rationale: This function expands to the host-specific shell command used +# to remove some files. +# ----------------------------------------------------------------------------- +ifeq ($(HOST_OS),windows) +host-rm = \ + $(eval __host_rm_files := $(foreach __host_rm_file,$1,$(subst /,\,$(wildcard $(__host_rm_file)))))\ + $(if $(__host_rm_files),del /f/q $(__host_rm_files) >NUL 2>NUL || rem) +else +host-rm = rm -f $1 +endif + +# +# Copyright (C) YuqiaoZhang(HanetakaChou) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# + +# ANDROID_SDK_BUILD_TOOLS_VERSION := 33.0.2 +# APP_PLATFORM:=android-28 + +HIDE := @ + +LOCAL_PATH := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) +KEYSTORE_DIR := $(LOCAL_PATH)/keystore +RES_DIR := $(LOCAL_PATH)/res +FLAT_DIR := $(LOCAL_PATH)/flat +APK_DIR := $(LOCAL_PATH)/apk +ANDROID_JAR_PATH := $(ANDROID_SDK_DIR)/platforms/$(APP_PLATFORM)/android.jar +ifeq ($(OS),Windows_NT) +AAPT2_PATH := $(ANDROID_SDK_DIR)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/aapt2.exe +ZIPALIGN_PATH := $(ANDROID_SDK_DIR)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/zipalign.exe +APKSIGNER_PATH := $(ANDROID_SDK_DIR)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/apksigner.bat +JAR_PATH := $(JDK_DIR)/bin/jar.exe +KEYTOOL_PATH := $(JDK_DIR)/bin/keytool.exe +else +AAPT2_PATH := $(ANDROID_SDK_DIR)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/aapt2 +ZIPALIGN_PATH := $(ANDROID_SDK_DIR)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/zipalign +APKSIGNER_PATH := $(ANDROID_SDK_DIR)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/apksigner +JAR_PATH := $(JDK_DIR)/bin/jar +KEYTOOL_PATH := $(JDK_DIR)/bin/keytool +endif + +all : $(APK_DIR)/demo-android.apk + +$(KEYSTORE_DIR)/demo-android-debug.keystore : + $(HIDE) $(call host-mkdir,$(KEYSTORE_DIR)) + $(HIDE) "$(KEYTOOL_PATH)" -genkey -v -keystore "$(KEYSTORE_DIR)/demo-android-debug.keystore" -storepass demo-android -storetype PKCS12 -keypass demo-android -alias demo-android-debug -keyalg RSA -keysize 3072 -validity 1024 -dname "CN=Demo-Android" + +$(FLAT_DIR)/values_styles.arsc.flat : $(RES_DIR)/values/styles.xml + $(HIDE) $(call host-mkdir,$(FLAT_DIR)) + $(HIDE) "$(AAPT2_PATH)" compile -o "$(FLAT_DIR)" "$(RES_DIR)\values\styles.xml" + +$(APK_DIR)/demo-android-unaligned.apk : $(addprefix $(LOCAL_PATH)/debug_$(APP_DEBUG)/lib/, $(addsuffix /libNativeActivity.so, $(APP_ABI))) $(FLAT_DIR)/values_styles.arsc.flat $(LOCAL_PATH)/AndroidManifest.xml + $(HIDE) $(call host-mkdir,$(APK_DIR)) + $(HIDE) "$(AAPT2_PATH)" link --debug-mode -I "$(ANDROID_JAR_PATH)" "$(FLAT_DIR)/values_styles.arsc.flat" -o "$(APK_DIR)/demo-android-unaligned.apk" --manifest "$(LOCAL_PATH)/AndroidManifest.xml" + $(HIDE) "$(JAR_PATH)" uf "$(APK_DIR)/demo-android-unaligned.apk" -C "$(LOCAL_PATH)/debug_$(APP_DEBUG)" lib + +$(APK_DIR)/demo-android-unsigned.apk : $(APK_DIR)/demo-android-unaligned.apk + $(HIDE) $(call host-mkdir,$(APK_DIR)) + $(HIDE) "$(ZIPALIGN_PATH)" -f 4 "$(APK_DIR)/demo-android-unaligned.apk" "$(APK_DIR)/demo-android-unsigned.apk" + +$(APK_DIR)/demo-android.apk : $(KEYSTORE_DIR)/demo-android-debug.keystore $(APK_DIR)/demo-android-unsigned.apk + $(HIDE) $(call host-mkdir,$(APK_DIR)) + $(HIDE) $(call host-cp,$(APK_DIR)/demo-android-unsigned.apk,$(APK_DIR)/demo-android.apk) + $(HIDE) "$(APKSIGNER_PATH)" sign -v --ks "$(KEYSTORE_DIR)/demo-android-debug.keystore" --ks-pass pass:demo-android -ks-key-alias demo-android-debug "$(APK_DIR)/demo-android.apk" + +clean: + $(HIDE) $(call host-rm,$(KEYSTORE_DIR)/demo-android-debug.keystore) + $(HIDE) $(call host-rm,$(FLAT_DIR)/values_styles.arsc.flat) + $(HIDE) $(call host-rm,$(APK_DIR)/demo-android-unaligned.apk) + $(HIDE) $(call host-rm,$(APK_DIR)/demo-android-unsigned.apk) + $(HIDE) $(call host-rm,$(APK_DIR)/demo-android.apk) + +.PHONY : \ + all \ + clean \ No newline at end of file diff --git a/build-android/Android.mk b/build-android/Android.mk new file mode 100644 index 0000000..ffb6039 --- /dev/null +++ b/build-android/Android.mk @@ -0,0 +1,101 @@ +# +# Copyright (C) YuqiaoZhang(HanetakaChou) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# + +# https://developer.android.com/ndk/guides/android_mk + +LOCAL_PATH := $(call my-dir) + +# Android Application Library + +include $(CLEAR_VARS) + +LOCAL_MODULE := NativeActivity + +LOCAL_SRC_FILES := \ + $(LOCAL_PATH)/../source/support/camera_controller.cpp \ + $(LOCAL_PATH)/../source/support/main.cpp \ + $(LOCAL_PATH)/../source/support/renderer.cpp \ + $(LOCAL_PATH)/../source/support/tick_count.cpp \ + $(LOCAL_PATH)/../source/demo.cpp \ + $(LOCAL_PATH)/../thirdparty/ImportAsset/source/import_asset_file_input_stream.cpp \ + $(LOCAL_PATH)/../thirdparty/ImportAsset/source/import_asset_memory_input_stream.cpp \ + $(LOCAL_PATH)/../thirdparty/ImportAsset/source/import_dds_image_asset.cpp \ + $(LOCAL_PATH)/../thirdparty/ImportAsset/source/import_pvr_image_asset.cpp \ + $(LOCAL_PATH)/../thirdparty/ImportAsset/source/import_gltf_scene_asset_cgltf.cpp \ + $(LOCAL_PATH)/../thirdparty/ImportAsset/source/import_gltf_scene_asset.cpp \ + $(LOCAL_PATH)/../thirdparty/ImportAsset/source/import_asset_malloc.cpp + +ifeq (arm, $(TARGET_ARCH)) +LOCAL_ARM_MODE := arm +LOCAL_ARM_NEON := true +endif + +LOCAL_CFLAGS := +# LOCAL_CFLAGS += -finput-charset=UTF-8 -fexec-charset=UTF-8 +# LOCAL_CFLAGS += -fvisibility=hidden +LOCAL_CFLAGS += -Wall -Werror=return-type +LOCAL_CFLAGS += -Dbrx_init_unknown_device=brx_init_vk_device +LOCAL_CFLAGS += -Dbrx_destroy_unknown_device=brx_destroy_vk_device +LOCAL_CFLAGS += -DPAL_STDCPP_COMPAT=1 + +# LOCAL_CPPFLAGS := +# LOCAL_CPPFLAGS += -std=c++11 + +LOCAL_C_INCLUDES := +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../spirv +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../thirdparty/CoreRT/src/Native/inc/unix +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../thirdparty/DirectXMath/Inc + +LOCAL_LDFLAGS := +# LOCAL_LDFLAGS += -finput-charset=UTF-8 -fexec-charset=UTF-8 +# the "dynamic linker" can't recognize the old dtags +LOCAL_LDFLAGS += -Wl,--enable-new-dtags +# the "chrpath" can only make path shorter +# LOCAL_LDFLAGS += -Wl,-rpath,XORIGIN +LOCAL_LDFLAGS += -Wl,--version-script,$(LOCAL_PATH)/libNativeActivity.map + +LOCAL_LDLIBS := +LOCAL_LDLIBS += -landroid + +LOCAL_SHARED_LIBRARIES := +LOCAL_SHARED_LIBRARIES := BRX + +include $(BUILD_SHARED_LIBRARY) + +# BRX + +include $(CLEAR_VARS) + +LOCAL_MODULE := BRX + +LOCAL_SRC_FILES := $(LOCAL_PATH)/../thirdparty/Brioche/build-android/debug_$(APP_DEBUG)/lib/$(TARGET_ARCH_ABI)/libBRX$(TARGET_SONAME_EXTENSION) + +include $(PREBUILT_SHARED_LIBRARY) + +# VkLayer_khronos_validation + +ifeq (true, $(APP_DEBUG)) + +include $(CLEAR_VARS) + +LOCAL_MODULE := VkLayer_khronos_validation + +LOCAL_SRC_FILES := $(LOCAL_PATH)/../thirdparty/Brioche/thirdparty/Vulkan-ValidationLayers/bin/android/$(TARGET_ARCH_ABI)/libVkLayer_khronos_validation$(TARGET_SONAME_EXTENSION) + +include $(PREBUILT_SHARED_LIBRARY) + +endif diff --git a/build-android/AndroidManifest.xml b/build-android/AndroidManifest.xml new file mode 100644 index 0000000..48c8d6a --- /dev/null +++ b/build-android/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build-android/Application.mk b/build-android/Application.mk new file mode 100644 index 0000000..3e4399e --- /dev/null +++ b/build-android/Application.mk @@ -0,0 +1,24 @@ +# +# Copyright (C) YuqiaoZhang(HanetakaChou) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# + +# https://developer.android.com/ndk/guides/application_mk + +NDK_TOOLCHAIN_VERSION := clang +# APP_ABI := x86_64 x86 arm64-v8a armeabi-v7a +# APP_DEBUG:= true +# APP_PLATFORM := android-28 +# APP_STL := c++_static \ No newline at end of file diff --git a/build-android/Demo-Android.sln b/build-android/Demo-Android.sln new file mode 100644 index 0000000..57f9c57 --- /dev/null +++ b/build-android/Demo-Android.sln @@ -0,0 +1,64 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31314.256 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demo-Android", "Demo-Android.vcxproj", "{2610E7F8-A7DA-4314-A677-A88E5E91432F}" + ProjectSection(ProjectDependencies) = postProject + {719A58B7-8373-4C08-988E-A21F43E7EE23} = {719A58B7-8373-4C08-988E-A21F43E7EE23} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BRX-Android", "..\thirdparty\Brioche\build-android\BRX-Android.vcxproj", "{719A58B7-8373-4C08-988E-A21F43E7EE23}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Android-arm64-v8a = Debug|Android-arm64-v8a + Debug|Android-armeabi-v7a = Debug|Android-armeabi-v7a + Debug|Android-x86 = Debug|Android-x86 + Debug|Android-x86_64 = Debug|Android-x86_64 + Release|Android-arm64-v8a = Release|Android-arm64-v8a + Release|Android-armeabi-v7a = Release|Android-armeabi-v7a + Release|Android-x86 = Release|Android-x86 + Release|Android-x86_64 = Release|Android-x86_64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Debug|Android-arm64-v8a.ActiveCfg = Debug|Android-arm64-v8a + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Debug|Android-arm64-v8a.Build.0 = Debug|Android-arm64-v8a + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Debug|Android-armeabi-v7a.ActiveCfg = Debug|Android-armeabi-v7a + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Debug|Android-armeabi-v7a.Build.0 = Debug|Android-armeabi-v7a + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Debug|Android-x86.ActiveCfg = Debug|Android-x86 + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Debug|Android-x86.Build.0 = Debug|Android-x86 + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Debug|Android-x86_64.ActiveCfg = Debug|Android-x86_64 + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Debug|Android-x86_64.Build.0 = Debug|Android-x86_64 + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Release|Android-arm64-v8a.ActiveCfg = Release|Android-arm64-v8a + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Release|Android-arm64-v8a.Build.0 = Release|Android-arm64-v8a + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Release|Android-armeabi-v7a.ActiveCfg = Release|Android-armeabi-v7a + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Release|Android-armeabi-v7a.Build.0 = Release|Android-armeabi-v7a + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Release|Android-x86.ActiveCfg = Release|Android-x86 + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Release|Android-x86.Build.0 = Release|Android-x86 + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Release|Android-x86_64.ActiveCfg = Release|Android-x86_64 + {2610E7F8-A7DA-4314-A677-A88E5E91432F}.Release|Android-x86_64.Build.0 = Release|Android-x86_64 + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Debug|Android-arm64-v8a.ActiveCfg = Debug|Android-arm64-v8a + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Debug|Android-arm64-v8a.Build.0 = Debug|Android-arm64-v8a + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Debug|Android-armeabi-v7a.ActiveCfg = Debug|Android-armeabi-v7a + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Debug|Android-armeabi-v7a.Build.0 = Debug|Android-armeabi-v7a + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Debug|Android-x86.ActiveCfg = Debug|Android-x86 + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Debug|Android-x86.Build.0 = Debug|Android-x86 + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Debug|Android-x86_64.ActiveCfg = Debug|Android-x86_64 + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Debug|Android-x86_64.Build.0 = Debug|Android-x86_64 + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Release|Android-arm64-v8a.ActiveCfg = Release|Android-arm64-v8a + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Release|Android-arm64-v8a.Build.0 = Release|Android-arm64-v8a + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Release|Android-armeabi-v7a.ActiveCfg = Release|Android-armeabi-v7a + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Release|Android-armeabi-v7a.Build.0 = Release|Android-armeabi-v7a + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Release|Android-x86.ActiveCfg = Release|Android-x86 + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Release|Android-x86.Build.0 = Release|Android-x86 + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Release|Android-x86_64.ActiveCfg = Release|Android-x86_64 + {719A58B7-8373-4C08-988E-A21F43E7EE23}.Release|Android-x86_64.Build.0 = Release|Android-x86_64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3142C50D-495E-471E-A6B1-1E555B3ADA9B} + EndGlobalSection +EndGlobal diff --git a/build-android/Demo-Android.vcxproj b/build-android/Demo-Android.vcxproj new file mode 100644 index 0000000..5a07ff7 --- /dev/null +++ b/build-android/Demo-Android.vcxproj @@ -0,0 +1,308 @@ + + + + + Debug + Android-arm64-v8a + + + Debug + Android-armeabi-v7a + + + Debug + Android-x86 + + + Debug + Android-x86_64 + + + Release + Android-arm64-v8a + + + Release + Android-armeabi-v7a + + + Release + Android-x86 + + + Release + Android-x86_64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 15.0 + {2610E7F8-A7DA-4314-A677-A88E5E91432F} + MakeFileProj + Demo-Android + 10.0.18362.0 + + + + true + 25.2.9519653 + Makefile + + + false + 25.2.9519653 + Makefile + + + true + 25.2.9519653 + Makefile + + + false + 25.2.9519653 + Makefile + + + 25.2.9519653 + Makefile + + + 25.2.9519653 + Makefile + + + 25.2.9519653 + Makefile + + + 25.2.9519653 + Makefile + + + + + + + + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" -B +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM :=android-28" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean + $(ProjectDir)\apk\demo-android.apk + $(ProjectDir)\..\thirdparty\Brioche\build-android\obj\local\$(PlatformTarget);$(ProjectDir)\obj\local\$(PlatformTarget) + $(SolutionDir)\bin-agde\$(Platform)\$(Configuration)\ + $(SolutionDir)\obj-agde\$(Platform)\$(Configuration)\ + + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" -B +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM :=android-28" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean + $(ProjectDir)\apk\demo-android.apk + $(ProjectDir)\..\thirdparty\Brioche\build-android\obj\local\$(PlatformTarget);$(ProjectDir)\obj\local\$(PlatformTarget) + $(SolutionDir)\bin-agde\$(Platform)\$(Configuration)\ + $(SolutionDir)\obj-agde\$(Platform)\$(Configuration)\ + + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" -B +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM :=android-28" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean + $(ProjectDir)\apk\demo-android.apk + $(ProjectDir)\..\thirdparty\Brioche\build-android\obj\local\$(PlatformTarget);$(ProjectDir)\obj\local\$(PlatformTarget) + $(SolutionDir)\bin-agde\$(Platform)\$(Configuration)\ + $(SolutionDir)\obj-agde\$(Platform)\$(Configuration)\ + + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" -B +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM :=android-28" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean + $(ProjectDir)\apk\demo-android.apk + $(ProjectDir)\..\thirdparty\Brioche\build-android\obj\local\$(PlatformTarget);$(ProjectDir)\obj\local\$(PlatformTarget) + $(SolutionDir)\bin-agde\$(Platform)\$(Configuration)\ + $(SolutionDir)\obj-agde\$(Platform)\$(Configuration)\ + + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" -B +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM :=android-28" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean + $(ProjectDir)\apk\demo-android.apk + $(ProjectDir)\..\thirdparty\Brioche\build-android\obj\local\$(PlatformTarget);$(ProjectDir)\obj\local\$(PlatformTarget) + $(SolutionDir)\bin-agde\$(Platform)\$(Configuration)\ + $(SolutionDir)\obj-agde\$(Platform)\$(Configuration)\ + + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" -B +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM :=android-28" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean + $(ProjectDir)\apk\demo-android.apk + $(ProjectDir)\..\thirdparty\Brioche\build-android\obj\local\$(PlatformTarget);$(ProjectDir)\obj\local\$(PlatformTarget) + $(SolutionDir)\bin-agde\$(Platform)\$(Configuration)\ + $(SolutionDir)\obj-agde\$(Platform)\$(Configuration)\ + + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" -B +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM :=android-28" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean + $(ProjectDir)\apk\demo-android.apk + $(ProjectDir)\..\thirdparty\Brioche\build-android\obj\local\$(PlatformTarget);$(ProjectDir)\obj\local\$(PlatformTarget) + $(SolutionDir)\bin-agde\$(Platform)\$(Configuration)\ + $(SolutionDir)\obj-agde\$(Platform)\$(Configuration)\ + + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_DEBUG:=$(UseDebugLibraries)" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" -B +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" "ANDROID_SDK_DIR:=$(AndroidSdk)\." "JDK_DIR=$(AGDE_JAVA_HOME)\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" + + call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\APK.mk" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\ndk-build" -C "$(ProjectDir)\." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_$(UseDebugLibraries)\obj" "NDK_LIBS_OUT:=.\debug_$(UseDebugLibraries)\lib" "APP_ABI:=$(PlatformTarget)" "APP_PLATFORM :=android-28" clean +call "$(AndroidSdk)\ndk\$(AndroidNdkVersion)\prebuilt\windows-x86_64\bin\make" -C "$(ProjectDir)\." -f ".\GLSL.mk" clean + $(ProjectDir)\apk\demo-android.apk + $(ProjectDir)\..\thirdparty\Brioche\build-android\obj\local\$(PlatformTarget);$(ProjectDir)\obj\local\$(PlatformTarget) + $(SolutionDir)\bin-agde\$(Platform)\$(Configuration)\ + $(SolutionDir)\obj-agde\$(Platform)\$(Configuration)\ + + + + + + + + + + + + + + + + + + + + $(CleanDependson) + + + + + + \ No newline at end of file diff --git a/build-android/Demo-Android.vcxproj.filters b/build-android/Demo-Android.vcxproj.filters new file mode 100644 index 0000000..5d18170 --- /dev/null +++ b/build-android/Demo-Android.vcxproj.filters @@ -0,0 +1,160 @@ + + + + + + {d3d44437-e70c-46bd-8c20-c2cd514ac0e9} + + + {34b12179-03f8-4980-bf41-3d511b13a773} + + + {736508dc-56c9-4d65-be52-be367b671481} + + + {fdeb210d-c2e5-4c62-97d5-6dffea2f65d1} + + + {b61d08d5-1eee-4c9e-ac04-30cdb0a92f1a} + + + {e04f4479-55dc-4c69-9e6c-a2db6e3fe81b} + + + {8254002f-9645-46a6-89ca-55f6c0754a01} + + + + + source\support + + + source\support + + + source\support + + + code + + + assets + + + assets + + + source\support + + + + + source\support + + + source\support + + + source\support + + + code + + + assets + + + assets + + + source\support + + + + + build-android + + + build-android + + + build-android + + + spirv + + + spirv + + + spirv + + + spirv + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders\support + + + shaders\support + + + shaders\support + + + build-android + + + + + build-android + + + \ No newline at end of file diff --git a/build-android/build-android.cmd b/build-android/build-android.cmd new file mode 100644 index 0000000..945b8f4 --- /dev/null +++ b/build-android/build-android.cmd @@ -0,0 +1,29 @@ +:: +:: Copyright (C) YuqiaoZhang(HanetakaChou) +:: +:: This program is free software: you can redistribute it and\or modify +:: it under the terms of the GNU Lesser General Public License as published +:: by the Free Software Foundation, either version 3 of the License, or +:: (at your option) any later version. +:: +:: This program is distributed in the hope that it will be useful, +:: but WITHOUT ANY WARRANTY; without even the implied warranty of +:: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +:: GNU Lesser General Public License for more details. +:: +:: You should have received a copy of the GNU Lesser General Public License +:: along with this program. If not, see . +:: + +@echo off + +:: SET ANDROID_NDK_ROOT=C:\Users\Public\Documents\android-sdk\ndk\25.2.9519653 +:: SET ANDROID_SDK_ROOT=C:\Users\Public\Documents\android-sdk +:: SET JAVA_HOME=C:\Users\Public\Documents\jdk-17 + +pushd %~dp0 +call "%~dp0..\thirdparty\Brioche\build-android\build-android.cmd" +call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "%~dp0..\build-GLSL" -f ".\GLSL.mk" "APP_DEBUG:=false" +call "%ANDROID_NDK_ROOT%\ndk-build" -C "%~dp0." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_false\obj" "NDK_LIBS_OUT:=.\debug_false\lib" "APP_DEBUG:=false" "APP_ABI:=arm64-v8a armeabi-v7a x86 x86_64" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" +call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "%~dp0." -f ".\APK.mk" "ANDROID_SDK_DIR:=%ANDROID_SDK_ROOT%\." "JDK_DIR:=%JAVA_HOME%\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=arm64-v8a armeabi-v7a x86 x86_64" "APP_PLATFORM:=android-33" +popd \ No newline at end of file diff --git a/build-android/clean-android.cmd b/build-android/clean-android.cmd new file mode 100644 index 0000000..1ee37c1 --- /dev/null +++ b/build-android/clean-android.cmd @@ -0,0 +1,29 @@ +:: +:: Copyright (C) YuqiaoZhang(HanetakaChou) +:: +:: This program is free software: you can redistribute it and\or modify +:: it under the terms of the GNU Lesser General Public License as published +:: by the Free Software Foundation, either version 3 of the License, or +:: (at your option) any later version. +:: +:: This program is distributed in the hope that it will be useful, +:: but WITHOUT ANY WARRANTY; without even the implied warranty of +:: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +:: GNU Lesser General Public License for more details. +:: +:: You should have received a copy of the GNU Lesser General Public License +:: along with this program. If not, see . +:: + +@echo off + +:: SET ANDROID_NDK_ROOT=C:\Users\Public\Documents\android-sdk\ndk\25.2.9519653 +:: SET ANDROID_SDK_ROOT=C:\Users\Public\Documents\android-sdk +:: SET JAVA_HOME=C:\Users\Public\Documents\jdk-17 + +pushd %~dp0 +call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "%~dp0." -f ".\APK.mk" clean +call "%ANDROID_NDK_ROOT%\ndk-build" -C "%~dp0." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_false\obj" "NDK_LIBS_OUT:=.\debug_false\lib" "APP_ABI:=armeabi-v7a arm64-v8a x86 x86_64" "APP_PLATFORM:=android-28" clean +call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "%~dp0..\build-GLSL" -f ".\GLSL.mk" clean +call "%~dp0..\thirdparty\Brioche\build-android\clean-android.cmd" +popd \ No newline at end of file diff --git a/build-android/libNativeActivity.map b/build-android/libNativeActivity.map new file mode 100644 index 0000000..de226bf --- /dev/null +++ b/build-android/libNativeActivity.map @@ -0,0 +1,23 @@ +/* + * Copyright (C) YuqiaoZhang(HanetakaChou) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +{ + global: + ANativeActivity_onCreate; + local: + *; +}; \ No newline at end of file diff --git a/build-android/rebuild-android.cmd b/build-android/rebuild-android.cmd new file mode 100644 index 0000000..123a77e --- /dev/null +++ b/build-android/rebuild-android.cmd @@ -0,0 +1,31 @@ +:: +:: Copyright (C) YuqiaoZhang(HanetakaChou) +:: +:: This program is free software: you can redistribute it and\or modify +:: it under the terms of the GNU Lesser General Public License as published +:: by the Free Software Foundation, either version 3 of the License, or +:: (at your option) any later version. +:: +:: This program is distributed in the hope that it will be useful, +:: but WITHOUT ANY WARRANTY; without even the implied warranty of +:: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +:: GNU Lesser General Public License for more details. +:: +:: You should have received a copy of the GNU Lesser General Public License +:: along with this program. If not, see . +:: + +@echo off + +:: SET ANDROID_NDK_ROOT=C:\Users\Public\Documents\android-sdk\ndk\25.2.9519653 +:: SET ANDROID_SDK_ROOT=C:\Users\Public\Documents\android-sdk +:: SET JAVA_HOME=C:\Users\Public\Documents\jdk-17 + +pushd %~dp0 +call "%~dp0..\thirdparty\Brioche\build-android\rebuild-android.cmd" +call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "%~dp0." -f ".\APK.mk" clean +call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "%~dp0." -f ".\GLSL.mk" clean +call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "%~dp0." -f ".\GLSL.mk" "APP_DEBUG:=false" +call "%ANDROID_NDK_ROOT%\ndk-build" -C "%~dp0." NDK_PROJECT_PATH:=null "NDK_APPLICATION_MK:=.\Application.mk" "APP_BUILD_SCRIPT:=.\Android.mk" "NDK_OUT:=.\debug_false\obj" "NDK_LIBS_OUT:=.\debug_false\lib" "APP_DEBUG:=false" "APP_ABI:=arm64-v8a armeabi-v7a x86 x86_64" "APP_PLATFORM:=android-28" "APP_STL:=c++_static" -B +call "%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make" -C "%~dp0." -f ".\APK.mk" "ANDROID_SDK_DIR:=%ANDROID_SDK_ROOT%\." "JDK_DIR:=%JAVA_HOME%\." "ANDROID_SDK_BUILD_TOOLS_VERSION:=33.0.2" "APP_ABI:=${{matrix.target_arch_abi}}" "APP_DEBUG:=${{matrix.use_debug_libraries}}" "APP_PLATFORM:=android-33" +popd \ No newline at end of file diff --git a/build-android/res/values/styles.xml b/build-android/res/values/styles.xml new file mode 100644 index 0000000..afd4e15 --- /dev/null +++ b/build-android/res/values/styles.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/build-linux/.gitignore b/build-linux/.gitignore new file mode 100644 index 0000000..4c7473d --- /dev/null +++ b/build-linux/.gitignore @@ -0,0 +1,2 @@ +/bin +/obj diff --git a/build-linux/Demo-Linux.map b/build-linux/Demo-Linux.map new file mode 100644 index 0000000..2df9b7d --- /dev/null +++ b/build-linux/Demo-Linux.map @@ -0,0 +1,21 @@ +/* + * Copyright (C) YuqiaoZhang(HanetakaChou) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +{ + local: + *; +}; \ No newline at end of file diff --git a/build-linux/Linux.mk b/build-linux/Linux.mk new file mode 100644 index 0000000..f1de7a6 --- /dev/null +++ b/build-linux/Linux.mk @@ -0,0 +1,233 @@ +# +# Copyright (C) YuqiaoZhang(HanetakaChou) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# + +HIDE := @ + +LOCAL_PATH := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) +ifeq (true, $(APP_DEBUG)) + BIN_DIR := $(LOCAL_PATH)/bin/debug + OBJ_DIR := $(LOCAL_PATH)/obj/debug +else + BIN_DIR := $(LOCAL_PATH)/bin/release + OBJ_DIR := $(LOCAL_PATH)/obj/release +endif +SOURCE_DIR := $(LOCAL_PATH)/../source +THIRD_PARTY_DIR := $(LOCAL_PATH)/../thirdparty + +CC := clang++ + +C_FLAGS := +C_FLAGS += -Wall -Werror=return-type +# C_FLAGS += -fvisibility=hidden +C_FLAGS += -fPIE -fPIC +C_FLAGS += -pthread +ifeq (true, $(APP_DEBUG)) + C_FLAGS += -g -O0 -UNDEBUG +else + C_FLAGS += -O2 -DNDEBUG +endif +C_FLAGS += -Dbrx_init_unknown_device=brx_init_vk_device +C_FLAGS += -Dbrx_destroy_unknown_device=brx_destroy_vk_device +C_FLAGS += -I$(LOCAL_PATH)/../spirv +C_FLAGS += -DPAL_STDCPP_COMPAT=1 +C_FLAGS += -I$(THIRD_PARTY_DIR)/CoreRT/src/Native/inc/unix +C_FLAGS += -I$(THIRD_PARTY_DIR)/DirectXMath/Inc + +LD_FLAGS := +LD_FLAGS += -pthread +LD_FLAGS += -Wl,--no-undefined +LD_FLAGS += -Wl,--enable-new-dtags +LD_FLAGS += -Wl,-rpath,'$$ORIGIN' +ifneq (true, $(APP_DEBUG)) + LD_FLAGS += -s +endif + +all : \ + $(BIN_DIR)/glTF-Viewer + +# Link +ifeq (true, $(APP_DEBUG)) +$(BIN_DIR)/glTF-Viewer: \ + $(OBJ_DIR)/Demo-support-camera_controller.o \ + $(OBJ_DIR)/Demo-support-main.o \ + $(OBJ_DIR)/Demo-support-renderer.o \ + $(OBJ_DIR)/Demo-support-tick_count.o \ + $(OBJ_DIR)/Demo-demo.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_file_input_stream.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_memory_input_stream.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_dds_image_asset.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_pvr_image_asset.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset_cgltf.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_malloc.o \ + $(BIN_DIR)/libBRX.so \ + $(BIN_DIR)/libVkLayer_khronos_validation.so \ + $(BIN_DIR)/VkLayer_khronos_validation.json \ + $(LOCAL_PATH)/Demo-Linux.map +else +$(BIN_DIR)/glTF-Viewer: \ + $(OBJ_DIR)/Demo-support-camera_controller.o \ + $(OBJ_DIR)/Demo-support-main.o \ + $(OBJ_DIR)/Demo-support-renderer.o \ + $(OBJ_DIR)/Demo-support-tick_count.o \ + $(OBJ_DIR)/Demo-demo.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_file_input_stream.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_memory_input_stream.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_dds_image_asset.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_pvr_image_asset.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset_cgltf.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_malloc.o \ + $(BIN_DIR)/libBRX.so \ + $(LOCAL_PATH)/Demo-Linux.map +endif + $(HIDE) mkdir -p $(BIN_DIR) + $(HIDE) $(CC) -pie $(LD_FLAGS) \ + -Wl,--version-script=$(LOCAL_PATH)/Demo-Linux.map \ + $(OBJ_DIR)/Demo-support-camera_controller.o \ + $(OBJ_DIR)/Demo-support-main.o \ + $(OBJ_DIR)/Demo-support-renderer.o \ + $(OBJ_DIR)/Demo-support-tick_count.o \ + $(OBJ_DIR)/Demo-demo.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_file_input_stream.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_memory_input_stream.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_dds_image_asset.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_pvr_image_asset.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset_cgltf.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset.o \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_malloc.o \ + -L$(BIN_DIR) -lBRX \ + -lxcb \ + -o $(BIN_DIR)/glTF-Viewer + +# Compile +$(OBJ_DIR)/Demo-support-camera_controller.o: $(SOURCE_DIR)/support/camera_controller.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/support/camera_controller.cpp -MD -MF $(OBJ_DIR)/Demo-support-camera_controller.d -o $(OBJ_DIR)/Demo-support-camera_controller.o + +$(OBJ_DIR)/Demo-support-main.o: $(SOURCE_DIR)/support/main.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/support/main.cpp -MD -MF $(OBJ_DIR)/Demo-support-main.d -o $(OBJ_DIR)/Demo-support-main.o + +$(OBJ_DIR)/Demo-support-renderer.o: $(SOURCE_DIR)/support/renderer.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/support/renderer.cpp -MD -MF $(OBJ_DIR)/Demo-support-renderer.d -o $(OBJ_DIR)/Demo-support-renderer.o + +$(OBJ_DIR)/Demo-support-tick_count.o: $(SOURCE_DIR)/support/tick_count.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/support/tick_count.cpp -MD -MF $(OBJ_DIR)/Demo-support-tick_count.d -o $(OBJ_DIR)/Demo-support-tick_count.o + +$(OBJ_DIR)/Demo-demo.o: $(SOURCE_DIR)/demo.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/demo.cpp -MD -MF $(OBJ_DIR)/Demo-demo.d -o $(OBJ_DIR)/Demo-demo.o + +$(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_file_input_stream.o: $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_asset_file_input_stream.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_asset_file_input_stream.cpp -MD -MF $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_file_input_stream.d -o $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_file_input_stream.o + +$(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_memory_input_stream.o: $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_asset_memory_input_stream.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_asset_memory_input_stream.cpp -MD -MF $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_memory_input_stream.d -o $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_memory_input_stream.o + +$(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_dds_image_asset.o: $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_dds_image_asset.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_dds_image_asset.cpp -MD -MF $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_dds_image_asset.d -o $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_dds_image_asset.o + +$(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_pvr_image_asset.o: $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_pvr_image_asset.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_pvr_image_asset.cpp -MD -MF $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_pvr_image_asset.d -o $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_pvr_image_asset.o + +$(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset_cgltf.o: $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_gltf_scene_asset_cgltf.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_gltf_scene_asset_cgltf.cpp -MD -MF $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset_cgltf.d -o $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset_cgltf.o + +$(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset.o: $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_gltf_scene_asset.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_gltf_scene_asset.cpp -MD -MF $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset.d -o $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset.o + +$(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_malloc.o: $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_asset_malloc.cpp + $(HIDE) mkdir -p $(OBJ_DIR) + $(HIDE) $(CC) -c $(C_FLAGS) $(SOURCE_DIR)/../thirdparty/ImportAsset/source/import_asset_malloc.cpp -MD -MF $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_malloc.d -o $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_malloc.o + +# Copy +ifeq (true, $(APP_DEBUG)) +CONFIG_NAME := debug +else +CONFIG_NAME := release +endif +$(BIN_DIR)/libBRX.so: $(THIRD_PARTY_DIR)/Brioche/build-linux/bin/$(CONFIG_NAME)/libBRX.so + $(HIDE) mkdir -p $(BIN_DIR) + $(HIDE) cp -f $(THIRD_PARTY_DIR)/Brioche/build-linux/bin/$(CONFIG_NAME)/libBRX.so $(BIN_DIR)/libBRX.so + + +$(BIN_DIR)/libVkLayer_khronos_validation.so: $(THIRD_PARTY_DIR)/Brioche/thirdparty/Vulkan-ValidationLayers/bin/linux/x64/libVkLayer_khronos_validation.so + $(HIDE) mkdir -p $(BIN_DIR) + $(HIDE) cp -f $(THIRD_PARTY_DIR)/Brioche/thirdparty/Vulkan-ValidationLayers/bin/linux/x64/libVkLayer_khronos_validation.so $(BIN_DIR)/libVkLayer_khronos_validation.so + +$(BIN_DIR)/VkLayer_khronos_validation.json: $(THIRD_PARTY_DIR)/Brioche/thirdparty/Vulkan-ValidationLayers/bin/linux/x64/VkLayer_khronos_validation.json + $(HIDE) mkdir -p $(BIN_DIR) + $(HIDE) cp -f $(THIRD_PARTY_DIR)/Brioche/thirdparty/Vulkan-ValidationLayers/bin/linux/x64/VkLayer_khronos_validation.json $(BIN_DIR)/VkLayer_khronos_validation.json + +-include \ + $(OBJ_DIR)/Demo-support-camera_controller.d \ + $(OBJ_DIR)/Demo-support-main.d \ + $(OBJ_DIR)/Demo-support-renderer.d \ + $(OBJ_DIR)/Demo-support-tick_count.d \ + $(OBJ_DIR)/Demo-demo.d \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_file_input_stream.d \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_memory_input_stream.d \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_dds_image_asset.d \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_pvr_image_asset.d \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset_cgltf.d \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset.d \ + $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_malloc.d + +clean: + $(HIDE) rm -f $(BIN_DIR)/glTF-Viewer + $(HIDE) rm -f $(OBJ_DIR)/Demo-support-camera_controller.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-support-main.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-support-renderer.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-support-tick_count.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-demo.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_file_input_stream.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_memory_input_stream.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_dds_image_asset.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_pvr_image_asset.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset_cgltf.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_malloc.o + $(HIDE) rm -f $(OBJ_DIR)/Demo-support-camera_controller.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-support-main.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-support-renderer.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-support-tick_count.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-demo.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_file_input_stream.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_memory_input_stream.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_dds_image_asset.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_pvr_image_asset.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset_cgltf.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_gltf_scene_asset.d + $(HIDE) rm -f $(OBJ_DIR)/Demo-thirdparty-ImportAsset-import_asset_malloc.d + $(HIDE) rm -f $(BIN_DIR)/libBRX.so +ifeq (true, $(APP_DEBUG)) + $(HIDE) rm -f $(BIN_DIR)/libVkLayer_khronos_validation.so + $(HIDE) rm -f $(BIN_DIR)/VkLayer_khronos_validation.json +endif + +.PHONY : \ + all \ + clean \ No newline at end of file diff --git a/build-windows/.gitignore b/build-windows/.gitignore new file mode 100644 index 0000000..b9884c6 --- /dev/null +++ b/build-windows/.gitignore @@ -0,0 +1,4 @@ +/.vs +/bin +/obj +/*.vcxproj.user diff --git a/build-windows/Demo-Windows-D3D12.vcxproj b/build-windows/Demo-Windows-D3D12.vcxproj new file mode 100644 index 0000000..c24a641 --- /dev/null +++ b/build-windows/Demo-Windows-D3D12.vcxproj @@ -0,0 +1,313 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + Pixel + Pixel + Pixel + Pixel + + + Document + Vertex + Vertex + Vertex + Vertex + + + Document + Pixel + Pixel + Pixel + Pixel + + + + + + Document + Vertex + Vertex + Vertex + Vertex + + + + + + Document + Pixel + Pixel + Pixel + Pixel + + + Document + Compute + Compute + Compute + Compute + + + + + Document + Vertex + Vertex + Vertex + Vertex + + + + + + + + + {78fae1fa-0a6a-4408-9285-030a876b0649} + + + + 15.0 + {8515DB3F-23BB-424F-A356-2967FF78E83E} + Win32Proj + Demo-Windows-D3D12 + 10.0 + Demo-Windows-D3D12 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)\bin\$(Platform)\$(Configuration)\ + glTF-Viewer-D3D12 + + + true + $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)\bin\$(Platform)\$(Configuration)\ + glTF-Viewer-D3D12 + + + true + $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)\bin\$(Platform)\$(Configuration)\ + glTF-Viewer-D3D12 + + + false + $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)\bin\$(Platform)\$(Configuration)\ + glTF-Viewer-D3D12 + + + + Level3 + MaxSpeed + true + true + true + brx_init_unknown_device=brx_init_d3d12_device;brx_destroy_unknown_device=brx_destroy_d3d12_device;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) + ..\dxbc; + + + true + true + true + Windows + wmainCRTStartup + comdlg32.lib;%(AdditionalDependencies) + + + 5.1 + $(SolutionDir)\..\dxbc\$(Configuration)\_internal_%(Filename).inl + + %(Filename)_shader_module_code + + + + + Level3 + Disabled + true + brx_init_unknown_device=brx_init_d3d12_device;brx_destroy_unknown_device=brx_destroy_d3d12_device;_WINDOWS;_DEBUG;WIN32;%(PreprocessorDefinitions) + ProgramDatabase + ..\dxbc; + + + true + Windows + + + + + 209715200 + 104857600 + wmainCRTStartup + comdlg32.lib;%(AdditionalDependencies) + + + 5.1 + $(SolutionDir)\..\dxbc\$(Configuration)\_internal_%(Filename).inl + + %(Filename)_shader_module_code + + + + + Level3 + Disabled + true + brx_init_unknown_device=brx_init_d3d12_device;brx_destroy_unknown_device=brx_destroy_d3d12_device;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) + ProgramDatabase + ..\dxbc; + + + true + Windows + + + + + wmainCRTStartup + comdlg32.lib;%(AdditionalDependencies) + + + 5.1 + $(SolutionDir)\..\dxbc\$(Configuration)\_internal_%(Filename).inl + + %(Filename)_shader_module_code + + + + + Level3 + MaxSpeed + true + true + true + brx_init_unknown_device=brx_init_d3d12_device;brx_destroy_unknown_device=brx_destroy_d3d12_device;_WINDOWS;NDEBUG;WIN32;%(PreprocessorDefinitions) + ..\dxbc; + + + true + true + true + Windows + wmainCRTStartup + comdlg32.lib;%(AdditionalDependencies) + + + 5.1 + $(SolutionDir)\..\dxbc\$(Configuration)\_internal_%(Filename).inl + + %(Filename)_shader_module_code + + + + + + \ No newline at end of file diff --git a/build-windows/Demo-Windows-D3D12.vcxproj.filters b/build-windows/Demo-Windows-D3D12.vcxproj.filters new file mode 100644 index 0000000..97a6941 --- /dev/null +++ b/build-windows/Demo-Windows-D3D12.vcxproj.filters @@ -0,0 +1,220 @@ + + + + + {6564cc1a-1902-4833-bf99-8a3fd8b5f6fb} + + + {0fdd0fa4-cdf6-47e5-9605-a438eac8a7bf} + + + {4ea7a279-3984-44fb-905e-7243713ff6d4} + + + {6b62e300-cff7-473b-81b7-b9828e035938} + + + {1395e396-5ae8-4bc3-87ac-db1a12e3c0db} + + + {baf2d192-809f-42f3-87ef-cc9338cc67e4} + + + {878cdbd0-309f-49fe-bec5-bf64a8ea8c95} + + + {588679ab-dc7d-4f65-8caf-0737dc99ecf4} + + + {898b0181-2305-475e-b862-b329b30e9416} + + + {f2a3a172-54d9-4c6d-8db2-64846e2fdbcb} + + + {bd6f1366-4849-4592-be66-d8fcc6590719} + + + {c3721dee-ffe8-4532-acfd-9cb41ad2f05a} + + + {aed9a659-d031-462c-b818-300863bb5b10} + + + {0a103360-b15d-49fc-87a4-e8d83c7bccfd} + + + + + source + + + source\support + + + source\support + + + source\support + + + source\support + + + thirdparty\ConvertUTF\source + + + thirdparty\ConvertUTF\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + + + source + + + source\support + + + source\support + + + source\support + + + source\support + + + thirdparty\ConvertUTF\include + + + thirdparty\ImportAsset\include + + + thirdparty\ImportAsset\include + + + thirdparty\ImportAsset\include + + + thirdparty\DLB + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + + + dxbc + + + dxbc + + + dxbc + + + shaders + + + shaders + + + dxbc + + + shaders + + + dxbc + + + shaders\support + + + shaders + + + dxbc + + + shaders + + + shaders + + + thirdparty\ImportAsset\shaders + + + thirdparty\ImportAsset\shaders + + + dxbc + + + thirdparty\DLB + + + thirdparty\DLB + + + + + shaders + + + shaders + + + shaders\support + + + shaders\support + + + shaders + + + shaders + + + shaders + + + \ No newline at end of file diff --git a/build-windows/Demo-Windows-VK.vcxproj b/build-windows/Demo-Windows-VK.vcxproj new file mode 100644 index 0000000..f72ba2d --- /dev/null +++ b/build-windows/Demo-Windows-VK.vcxproj @@ -0,0 +1,395 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -O -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -O -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\support\full_screen_transfer_pipeline_layout.sli + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\support\full_screen_transfer_pipeline_layout.sli + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\support\full_screen_transfer_pipeline_layout.sli + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\support\full_screen_transfer_pipeline_layout.sli + + + Document + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -O -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -O -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\support\full_screen_transfer_pipeline_layout.sli + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\support\full_screen_transfer_pipeline_layout.sli + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\support\full_screen_transfer_pipeline_layout.sli + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\support\full_screen_transfer_pipeline_layout.sli + + + + + + + Document + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + Compile GLSL %(Filename) + Compile GLSL %(Filename) + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\deferred_shading_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\deferred_shading_pipeline_resource_binding.sli + Compile GLSL %(Filename) + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\deferred_shading_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\deferred_shading_pipeline_resource_binding.sli + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + + + + Document + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\deferred_shading_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\deferred_shading_pipeline_resource_binding.sli + Compile GLSL %(Filename) + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\deferred_shading_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\deferred_shading_pipeline_resource_binding.sli + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + + + Document + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + Compile GLSL %(Filename) + Compile GLSL %(Filename) + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\gbuffer_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\gbuffer_pipeline_resource_binding.sli + Compile GLSL %(Filename) + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\gbuffer_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\gbuffer_pipeline_resource_binding.sli + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=frag -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + + + + Document + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\gbuffer_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\gbuffer_pipeline_resource_binding.sli + Compile GLSL %(Filename) + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\gbuffer_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\gbuffer_pipeline_resource_binding.sli + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=vert -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + + + + Document + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=comp -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=comp -g -O0 -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=comp -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + "$(SolutionDir)..\thirdparty\Brioche\thirdparty\shaderc\bin\win32\x64\glslc.exe" -std=310es -mfmt=num -fshader-stage=comp -o "$(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl" "%(FullPath)" + Compile GLSL %(Filename) + Compile GLSL %(Filename) + Compile GLSL %(Filename) + Compile GLSL %(Filename) + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)\..\spirv\$(Configuration)\_internal_%(Filename).inl + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\skin_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\skin_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\skin_pipeline_resource_binding.sli + $(SolutionDir)..\thirdparty\Brioche\shaders\brx_define.sli;$(SolutionDir)..\shaders\skin_pipeline_resource_binding.sli + + + + + Document + true + true + + + Document + true + true + + + Document + true + true + + + Document + true + true + + + + + + + + + + + + + + + + {78fae1fa-0a6a-4408-9285-030a876b0649} + + + + 15.0 + {00ACFB22-3872-4380-BA9D-E1DD24888EF4} + Win32Proj + Demo-Windows-VK + 10.0 + Demo-Windows-VK + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)\bin\$(Platform)\$(Configuration)\ + glTF-Viewer-VK + + + true + $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)\bin\$(Platform)\$(Configuration)\ + glTF-Viewer-VK + + + true + $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)\bin\$(Platform)\$(Configuration)\ + glTF-Viewer-VK + + + false + $(SolutionDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + $(SolutionDir)\bin\$(Platform)\$(Configuration)\ + glTF-Viewer-VK + + + + Level3 + MaxSpeed + true + true + true + brx_init_unknown_device=brx_init_vk_device;brx_destroy_unknown_device=brx_destroy_vk_device;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) + ..\spirv; + + + true + true + true + Windows + wmainCRTStartup + comdlg32.lib;%(AdditionalDependencies) + + + + + Level3 + Disabled + true + brx_init_unknown_device=brx_init_vk_device;brx_destroy_unknown_device=brx_destroy_vk_device;_WINDOWS;_DEBUG;WIN32;%(PreprocessorDefinitions) + ProgramDatabase + ..\spirv; + + + true + Windows + + + + + 209715200 + 104857600 + wmainCRTStartup + comdlg32.lib;%(AdditionalDependencies) + + + + + Level3 + Disabled + true + brx_init_unknown_device=brx_init_vk_device;brx_destroy_unknown_device=brx_destroy_vk_device;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) + ProgramDatabase + ..\spirv; + + + true + Windows + + + + + wmainCRTStartup + comdlg32.lib;%(AdditionalDependencies) + + + + + Level3 + MaxSpeed + true + true + true + brx_init_unknown_device=brx_init_vk_device;brx_destroy_unknown_device=brx_destroy_vk_device;_WINDOWS;NDEBUG;WIN32;%(PreprocessorDefinitions) + ..\spirv; + + + true + true + true + Windows + wmainCRTStartup + comdlg32.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/build-windows/Demo-Windows-VK.vcxproj.filters b/build-windows/Demo-Windows-VK.vcxproj.filters new file mode 100644 index 0000000..844128b --- /dev/null +++ b/build-windows/Demo-Windows-VK.vcxproj.filters @@ -0,0 +1,243 @@ + + + + + source + + + source\support + + + source\support + + + source\support + + + source\support + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ConvertUTF\source + + + thirdparty\ConvertUTF\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + + + source + + + source\support + + + source\support + + + source\support + + + source\support + + + thirdparty\ImportAsset\include + + + thirdparty\ImportAsset\include + + + thirdparty\ConvertUTF\include + + + thirdparty\ImportAsset\include + + + thirdparty\DLB + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + thirdparty\ImportAsset\source + + + + + shaders\support + + + shaders\support + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + + + thirdparty\Vulkan-ValidationLayers\x86 + + + thirdparty\Vulkan-ValidationLayers\x86 + + + thirdparty\Vulkan-ValidationLayers\x64 + + + thirdparty\Vulkan-ValidationLayers\x64 + + + + + spirv + + + spirv + + + shaders\support + + + spirv + + + spirv + + + shaders + + + shaders + + + shaders + + + shaders + + + spirv + + + spirv + + + shaders + + + shaders + + + thirdparty\ImportAsset\shaders + + + thirdparty\ImportAsset\shaders + + + spirv + + + thirdparty\DLB + + + thirdparty\DLB + + + + + {5ef8ee9f-5a80-48bd-82a4-0cec12b21719} + + + {f6b3b0f4-57bb-4f0f-b3c1-af7cb367457c} + + + {3fd46330-fb50-48b9-9af8-f2bd1292021a} + + + {0a393837-6c85-4124-89a5-28e5ae120ec4} + + + {8392cec9-8a4b-4f55-939a-fda75c196bb8} + + + {20e5e431-be5d-49a8-9c62-4973be7abee1} + + + {9a26aaf8-9e1c-45da-a6dc-54788eb50266} + + + {59f24b2c-072f-4fca-91b0-b70b46bd4124} + + + {acd6da07-575e-4359-9d68-a0998b90dd02} + + + {c65442bb-15e9-4bfc-bc0c-4c74edddd1f5} + + + {f090f88f-683a-427f-9207-32271dc25c91} + + + {4d5eedcd-4e13-4bfb-826d-97363f91be3e} + + + {7538861b-f7f1-439f-9192-ca28dbf261ad} + + + {b5d88abe-d344-44b6-9031-e70cc4549238} + + + {a65309de-7b7b-4e93-9b1d-5a4c03e6a9b9} + + + {a4b3dcc9-f61b-4648-9680-5f42eb48ade0} + + + {87c8f33e-e008-4862-9dcd-f4325a3c1c68} + + + \ No newline at end of file diff --git a/build-windows/Demo-Windows.sln b/build-windows/Demo-Windows.sln new file mode 100644 index 0000000..47573a9 --- /dev/null +++ b/build-windows/Demo-Windows.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31314.256 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demo-Windows-VK", "Demo-Windows-VK.vcxproj", "{00ACFB22-3872-4380-BA9D-E1DD24888EF4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BRX-Windows", "..\thirdparty\Brioche\build-windows\BRX-Windows.vcxproj", "{78FAE1FA-0A6A-4408-9285-030A876B0649}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demo-Windows-D3D12", "Demo-Windows-D3D12.vcxproj", "{8515DB3F-23BB-424F-A356-2967FF78E83E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {00ACFB22-3872-4380-BA9D-E1DD24888EF4}.Debug|x64.ActiveCfg = Debug|x64 + {00ACFB22-3872-4380-BA9D-E1DD24888EF4}.Debug|x64.Build.0 = Debug|x64 + {00ACFB22-3872-4380-BA9D-E1DD24888EF4}.Debug|x86.ActiveCfg = Debug|Win32 + {00ACFB22-3872-4380-BA9D-E1DD24888EF4}.Debug|x86.Build.0 = Debug|Win32 + {00ACFB22-3872-4380-BA9D-E1DD24888EF4}.Release|x64.ActiveCfg = Release|x64 + {00ACFB22-3872-4380-BA9D-E1DD24888EF4}.Release|x64.Build.0 = Release|x64 + {00ACFB22-3872-4380-BA9D-E1DD24888EF4}.Release|x86.ActiveCfg = Release|Win32 + {00ACFB22-3872-4380-BA9D-E1DD24888EF4}.Release|x86.Build.0 = Release|Win32 + {78FAE1FA-0A6A-4408-9285-030A876B0649}.Debug|x64.ActiveCfg = Debug|x64 + {78FAE1FA-0A6A-4408-9285-030A876B0649}.Debug|x64.Build.0 = Debug|x64 + {78FAE1FA-0A6A-4408-9285-030A876B0649}.Debug|x86.ActiveCfg = Debug|Win32 + {78FAE1FA-0A6A-4408-9285-030A876B0649}.Debug|x86.Build.0 = Debug|Win32 + {78FAE1FA-0A6A-4408-9285-030A876B0649}.Release|x64.ActiveCfg = Release|x64 + {78FAE1FA-0A6A-4408-9285-030A876B0649}.Release|x64.Build.0 = Release|x64 + {78FAE1FA-0A6A-4408-9285-030A876B0649}.Release|x86.ActiveCfg = Release|Win32 + {78FAE1FA-0A6A-4408-9285-030A876B0649}.Release|x86.Build.0 = Release|Win32 + {8515DB3F-23BB-424F-A356-2967FF78E83E}.Debug|x64.ActiveCfg = Debug|x64 + {8515DB3F-23BB-424F-A356-2967FF78E83E}.Debug|x64.Build.0 = Debug|x64 + {8515DB3F-23BB-424F-A356-2967FF78E83E}.Debug|x86.ActiveCfg = Debug|Win32 + {8515DB3F-23BB-424F-A356-2967FF78E83E}.Debug|x86.Build.0 = Debug|Win32 + {8515DB3F-23BB-424F-A356-2967FF78E83E}.Release|x64.ActiveCfg = Release|x64 + {8515DB3F-23BB-424F-A356-2967FF78E83E}.Release|x64.Build.0 = Release|x64 + {8515DB3F-23BB-424F-A356-2967FF78E83E}.Release|x86.ActiveCfg = Release|Win32 + {8515DB3F-23BB-424F-A356-2967FF78E83E}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C128871C-DA37-4E4F-A583-4089B7086626} + EndGlobalSection +EndGlobal diff --git a/dxbc/.gitignore b/dxbc/.gitignore new file mode 100644 index 0000000..70a618f --- /dev/null +++ b/dxbc/.gitignore @@ -0,0 +1,2 @@ +/debug +/release diff --git a/dxbc/deferred_shading_fragment.inl b/dxbc/deferred_shading_fragment.inl new file mode 100644 index 0000000..4978085 --- /dev/null +++ b/dxbc/deferred_shading_fragment.inl @@ -0,0 +1,7 @@ +#define BYTE uint8_t +#ifndef NDEBUG +#include "debug/_internal_deferred_shading_fragment.inl" +#else +#include "release/_internal_deferred_shading_fragment.inl" +#endif +#undef BYTE \ No newline at end of file diff --git a/dxbc/deferred_shading_vertex.inl b/dxbc/deferred_shading_vertex.inl new file mode 100644 index 0000000..2b940aa --- /dev/null +++ b/dxbc/deferred_shading_vertex.inl @@ -0,0 +1,7 @@ +#define BYTE uint8_t +#ifndef NDEBUG +#include "debug/_internal_deferred_shading_vertex.inl" +#else +#include "release/_internal_deferred_shading_vertex.inl" +#endif +#undef BYTE \ No newline at end of file diff --git a/dxbc/full_screen_transfer_fragment.inl b/dxbc/full_screen_transfer_fragment.inl new file mode 100644 index 0000000..5d58653 --- /dev/null +++ b/dxbc/full_screen_transfer_fragment.inl @@ -0,0 +1,7 @@ +#define BYTE uint8_t +#ifndef NDEBUG +#include "debug/_internal_full_screen_transfer_fragment.inl" +#else +#include "release/_internal_full_screen_transfer_fragment.inl" +#endif +#undef BYTE \ No newline at end of file diff --git a/dxbc/full_screen_transfer_vertex.inl b/dxbc/full_screen_transfer_vertex.inl new file mode 100644 index 0000000..91ddedb --- /dev/null +++ b/dxbc/full_screen_transfer_vertex.inl @@ -0,0 +1,7 @@ +#define BYTE uint8_t +#ifndef NDEBUG +#include "debug/_internal_full_screen_transfer_vertex.inl" +#else +#include "release/_internal_full_screen_transfer_vertex.inl" +#endif +#undef BYTE \ No newline at end of file diff --git a/dxbc/gbuffer_fragment.inl b/dxbc/gbuffer_fragment.inl new file mode 100644 index 0000000..6605c51 --- /dev/null +++ b/dxbc/gbuffer_fragment.inl @@ -0,0 +1,7 @@ +#define BYTE uint8_t +#ifndef NDEBUG +#include "debug/_internal_gbuffer_fragment.inl" +#else +#include "release/_internal_gbuffer_fragment.inl" +#endif +#undef BYTE \ No newline at end of file diff --git a/dxbc/gbuffer_vertex.inl b/dxbc/gbuffer_vertex.inl new file mode 100644 index 0000000..dd32572 --- /dev/null +++ b/dxbc/gbuffer_vertex.inl @@ -0,0 +1,7 @@ +#define BYTE uint8_t +#ifndef NDEBUG +#include "debug/_internal_gbuffer_vertex.inl" +#else +#include "release/_internal_gbuffer_vertex.inl" +#endif +#undef BYTE \ No newline at end of file diff --git a/dxbc/skin_compute.inl b/dxbc/skin_compute.inl new file mode 100644 index 0000000..b5954de --- /dev/null +++ b/dxbc/skin_compute.inl @@ -0,0 +1,7 @@ +#define BYTE uint8_t +#ifndef NDEBUG +#include "debug/_internal_skin_compute.inl" +#else +#include "release/_internal_skin_compute.inl" +#endif +#undef BYTE \ No newline at end of file diff --git a/shaders/common_constant.sli b/shaders/common_constant.sli new file mode 100644 index 0000000..d01bf1f --- /dev/null +++ b/shaders/common_constant.sli @@ -0,0 +1,38 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _COMMON_CONSTANT_SLI_ +#define _COMMON_CONSTANT_SLI_ 1 + +#define g_vertex_position_buffer_stride 12u +#define g_vertex_varying_buffer_stride 12u +#define g_vertex_joint_buffer_stride 12u +#define g_index_uint16_buffer_stride 2u +#define g_index_uint32_buffer_stride 4u + +#if defined(__cplusplus) +static_assert((sizeof(scene_mesh_vertex_position_binding)) == g_vertex_position_buffer_stride, ""); +static_assert(0U == (g_vertex_position_buffer_stride % 4U), ""); +static_assert((sizeof(scene_mesh_vertex_varying_binding)) == g_vertex_varying_buffer_stride, ""); +static_assert(0U == (g_vertex_varying_buffer_stride % 4U), ""); +static_assert((sizeof(scene_mesh_vertex_joint_binding)) == g_vertex_joint_buffer_stride, ""); +static_assert(0U == (g_vertex_joint_buffer_stride % 4U), ""); +static_assert((sizeof(uint16_t)) == g_index_uint16_buffer_stride, ""); +static_assert((sizeof(uint32_t)) == g_index_uint32_buffer_stride, ""); +#endif + +#endif \ No newline at end of file diff --git a/shaders/common_resource_binding.sli b/shaders/common_resource_binding.sli new file mode 100644 index 0000000..114c4b1 --- /dev/null +++ b/shaders/common_resource_binding.sli @@ -0,0 +1,36 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _COMMON_RESOURCE_BINDING_SLI_ +#define _COMMON_RESOURCE_BINDING_SLI_ 1 + +#include "../thirdparty/Brioche/shaders/brx_define.sli" + +brx_cbuffer(common_none_update_set_uniform_buffer_binding, 0, 0) +{ + brx_column_major brx_float4x4 g_view_transform; + brx_column_major brx_float4x4 g_projection_transform; + brx_column_major brx_float4x4 g_inverse_view_transform; + brx_column_major brx_float4x4 g_inverse_projection_transform; + + brx_float g_screen_width; + brx_float g_screen_height; + brx_float _unused_padding_1; + brx_float _unused_padding_2; +}; + +#endif \ No newline at end of file diff --git a/shaders/deferred_shading_fragment.sl b/shaders/deferred_shading_fragment.sl new file mode 100644 index 0000000..73ce954 --- /dev/null +++ b/shaders/deferred_shading_fragment.sl @@ -0,0 +1,180 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include "deferred_shading_pipeline_resource_binding.sli" +#include "../thirdparty/ImportAsset/shaders/packed_vector.sli" +#include "../thirdparty/ImportAsset/shaders/octahedron_mapping.sli" +#include "math_constant.sli" + +#define INVALID_GBUFFER_DEPTH 0.0 + +brx_root_signature(deferred_shading_root_signature_macro, deferred_shading_root_signature_name) +brx_early_depth_stencil +brx_pixel_shader_parameter_begin(main) +brx_pixel_shader_parameter_in_frag_coord brx_pixel_shader_parameter_split +brx_pixel_shader_parameter_out(brx_float4, out_color, 0) +brx_pixel_shader_parameter_end(main) +{ + // TODO: single pass deferred shading + + brx_float depth = brx_load_2d(g_depth_texture[0], brx_int3(brx_frag_coord.xy, 0)).x; + + brx_branch + if (INVALID_GBUFFER_DEPTH == depth) + { + out_color = brx_float4(0.0, 0.0, 0.0, 1.0); + return; + } + + brx_uint4 gbuffer = brx_load_2d(g_gbuffer_texture[0], brx_int3(brx_frag_coord.xy, 0)); + + brx_float3 shading_normal_world_space = octahedron_unmap(R16G16_SNORM_to_FLOAT2(gbuffer.x)); + brx_float3 base_color = R10G10B10A2_UNORM_to_FLOAT4(gbuffer.y).xyz; + brx_float2 metallic_roughness = R16G16_UNORM_to_FLOAT2(gbuffer.z); + brx_float metallic = metallic_roughness.x; + brx_float roughness = metallic_roughness.y; + brx_float3 emissive = R10G10B10A2_UNORM_to_FLOAT4(gbuffer.w).xyz; + + const brx_float dielectric_specular = 0.04; + // UE4: https://github.com/EpicGames/UnrealEngine/blob/4.21/Engine/Shaders/Private/MobileBasePassPixelShader.usf#L376 + brx_float3 f0 = brx_clamp((dielectric_specular - dielectric_specular * metallic) + base_color * metallic, 0.0, 1.0); + brx_float3 albedo = brx_clamp(base_color - base_color * metallic, 0.0, 1.0); + + brx_float3 surface_position_world_space; + { + brx_float2 uv = brx_frag_coord.xy / brx_float2(g_screen_width, g_screen_height); + + brx_float surface_position_depth = depth; + + brx_float3 surface_position_ndc_space = brx_float3(uv * brx_float2(2.0, -2.0) + brx_float2(-1.0, 1.0), surface_position_depth); + + brx_float4 surface_position_view_space_with_w = brx_mul(g_inverse_projection_transform, brx_float4(surface_position_ndc_space, 1.0)); + + brx_float3 surface_position_view_space = surface_position_view_space_with_w.xyz / surface_position_view_space_with_w.w; + + surface_position_world_space = brx_mul(g_inverse_view_transform, brx_float4(surface_position_view_space, 1.0)).xyz; + } + + brx_float3 camera_ray_origin = brx_mul(g_inverse_view_transform, brx_float4(0.0, 0.0, 0.0, 1.0)).xyz; + + // TODO: shadow + // TODO: environment lighting + brx_float3 outgoing_radiance = brx_float3(0.0, 0.0, 0.0); + + const brx_int incident_light_count = 8; + + const brx_float3 incident_illuminances[incident_light_count] = brx_array_constructor_begin(brx_float3, incident_light_count) + brx_float3(0.7, 0.7, 0.7) brx_array_constructor_split + brx_float3(0.7, 0.7, 0.7) brx_array_constructor_split + brx_float3(0.7, 0.7, 0.7) brx_array_constructor_split + brx_float3(0.7, 0.7, 0.7) brx_array_constructor_split + brx_float3(0.3, 0.3, 0.3) brx_array_constructor_split + brx_float3(0.3, 0.3, 0.3) brx_array_constructor_split + brx_float3(0.3, 0.3, 0.3) brx_array_constructor_split + brx_float3(0.3, 0.3, 0.3) + brx_array_constructor_end; + + const brx_float3 Ls[incident_light_count] = brx_array_constructor_begin(brx_float3, incident_light_count) + brx_float3( 1.0, 1.0, 1.0) brx_array_constructor_split + brx_float3( 1.0, 1.0, -1.0) brx_array_constructor_split + brx_float3(-1.0, 1.0, 1.0) brx_array_constructor_split + brx_float3(-1.0, 1.0, -1.0) brx_array_constructor_split + brx_float3( 1.0, -1.0, 1.0) brx_array_constructor_split + brx_float3( 1.0, -1.0, -1.0) brx_array_constructor_split + brx_float3(-1.0, -1.0, 1.0) brx_array_constructor_split + brx_float3(-1.0, -1.0, -1.0) + brx_array_constructor_end; + + brx_unroll + for(brx_int incident_light_index = 0; incident_light_index < incident_light_count; ++incident_light_index) + { + brx_float3 incident_illuminance = incident_illuminances[incident_light_index]; + brx_float3 L = brx_normalize(Ls[incident_light_index]); + + brx_float3 V = brx_normalize(camera_ray_origin - surface_position_world_space); + brx_float3 N = shading_normal_world_space; + brx_float3 H = normalize(L + V); + brx_float NdotL = brx_clamp(dot(N, L), 0.0, 1.0); + brx_float NdotH = brx_clamp(dot(N, H), 0.0, 1.0); + brx_float NdotV = brx_clamp(dot(N, V), 0.0, 1.0); + brx_float VdotH = brx_clamp(dot(V, H), 0.0, 1.0); + + brx_float3 brdf_diffuse; + { + // Lambert + + brdf_diffuse = (1.0 / M_PI) * albedo; + } + + brx_float3 brdf_specular; + { + // Trowbridge Reitz + + // Prevent the roughness to be zero + // https://github.com/EpicGames/UnrealEngine/blob/4.27/Engine/Shaders/Private/CapsuleLightIntegrate.ush#L94 + const brx_float cvar_global_min_roughness_override = 0.02; + roughness = brx_max(roughness, cvar_global_min_roughness_override); + + // Real-Time Rendering Fourth Edition / 9.8.1 Normal Distribution Functions: "In the Disney principled shading model, Burley[214] exposes the roughness control to users as g = r2, where r is the user-interface roughness parameter value between 0 and 1." + brx_float alpha = roughness * roughness; + + // Equation 9.41 of Real-Time Rendering Fourth Edition: "Although **Trowbridge-Reitz distribution** is technically the correct name" + // Equation 8.11 of PBR Book: https://pbr-book.org/3ed-2018/Reflection_Models/Microfacet_Models#MicrofacetDistributionFunctions + brx_float alpha2 = alpha * alpha; + brx_float denominator = 1.0 + NdotH * (NdotH * alpha2 - NdotH); + brx_float D = (1.0 / M_PI) * (alpha2 / (denominator * denominator)); + + // Lambda: + // Equation 8.13 of PBR Book: https://pbr-book.org/3ed-2018/Reflection_Models/Microfacet_Models#MaskingandShadowing + // Equation 9.42 of Real-Time Rendering Fourth Edition + // Figure 8.18 of PBR Book: https://pbr-book.org/3ed-2018/Reflection_Models/Microfacet_Models#MaskingandShadowing + // Lambda(V) = 0.5*(-1.0 + (1.0/NoV)*sqrt(alpha^2 + (1.0 - alpha^2)*NoV^2)) + // Lambda(L) = 0.5*(-1.0 + (1.0/NoL)*sqrt(alpha^2 + (1.0 - alpha^2)*NoL^2)) + + // G2 + // Equation 9.31 of Real-Time Rendering Fourth Edition + // PBR Book / 8.4.3 Masking and Shadowing: "A more accurate model can be derived assuming that microfacet visibility is more likely the higher up a given point on a microface" + // G2 = 1.0/(1.0 + Lambda(V) + Lambda(L)) = (2.0*NoV*NoL)/(NoL*sqrt(alpha^2 + (1.0 - alpha^2)*NoV^2) + NoV*sqrt(alpha^2 + (1.0 - alpha^2)*NoL^2)) + + // V = G2/(4.0*NoV*NoL) = 0.5/(NoL*sqrt(alpha^2 + (1.0 - alpha^2)*NoV^2) + NoV*sqrt(alpha^2 + (1.0 - alpha^2)*NoL^2)) + + // float alpha2 = alpha * alpha; + // float term_v = NdotL * sqrt(alpha2 + (1.0 - alpha2) * NdotV * NdotV); + // float term_l = NdotV * sqrt(alpha2 + (1.0 - alpha2) * NdotL * NdotL); + // UE: [Vis_SmithJointApprox](https://github.com/EpicGames/UnrealEngine/blob/4.27/Engine/Shaders/Private/BRDF.ush#L380) + brx_float term_v = NdotL * (alpha + (1.0 - alpha) * NdotV); + brx_float term_l = NdotV * (alpha + (1.0 - alpha) * NdotL); + brx_float V = (0.5 / (term_v + term_l)); + + // glTF Sample Renderer: [F_Schlick](https://github.com/KhronosGroup/glTF-Sample-Renderer/blob/e5646a2bf87b0871ba3f826fc2335fe117a11411/source/Renderer/shaders/brdf.glsl#L24) + const brx_float3 f90 = brx_float3(1.0, 1.0, 1.0); + + brx_float x = brx_clamp(1.0 - VdotH, 0.0, 1.0); + brx_float x2 = x * x; + brx_float x5 = x * x2 * x2; + brx_float3 F = f0 + (f90 - f0) * x5; + + brdf_specular = D * V * F; + } + + outgoing_radiance += (brdf_diffuse + brdf_specular) * (NdotL * incident_illuminance); + } + + outgoing_radiance += emissive; + + out_color = brx_float4(outgoing_radiance, 1.0); +} \ No newline at end of file diff --git a/shaders/deferred_shading_pipeline_resource_binding.sli b/shaders/deferred_shading_pipeline_resource_binding.sli new file mode 100644 index 0000000..60e23c5 --- /dev/null +++ b/shaders/deferred_shading_pipeline_resource_binding.sli @@ -0,0 +1,35 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _DEFERRED_SHADING_TRANSFER_PIPELINE_LAYOUT_SLI_ +#define _DEFERRED_SHADING_TRANSFER_PIPELINE_LAYOUT_SLI_ 1 + +#include "../thirdparty/Brioche/shaders/brx_define.sli" +#include "common_resource_binding.sli" + +brx_texture_2d_uint(g_gbuffer_texture, 0, 1, 1); + +brx_texture_2d(g_depth_texture, 0, 2, 1); + +#define deferred_shading_root_signature_macro \ + brx_root_signature_root_parameter_begin(deferred_shading_root_signature_name) \ + brx_root_signature_root_cbv(0, 0) brx_root_signature_root_parameter_split \ + brx_root_signature_root_descriptor_table_srv(0, 1, 1) brx_root_signature_root_parameter_split \ + brx_root_signature_root_descriptor_table_srv(0, 2, 1) \ + brx_root_signature_root_parameter_end + +#endif \ No newline at end of file diff --git a/shaders/deferred_shading_vertex.sl b/shaders/deferred_shading_vertex.sl new file mode 100644 index 0000000..c3499ff --- /dev/null +++ b/shaders/deferred_shading_vertex.sl @@ -0,0 +1,33 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include "deferred_shading_pipeline_resource_binding.sli" + +brx_root_signature(deferred_shading_root_signature_macro, deferred_shading_root_signature_name) +brx_vertex_shader_parameter_begin(main) +brx_vertex_shader_parameter_in_vertex_id brx_vertex_shader_parameter_split +brx_vertex_shader_parameter_out_position +brx_vertex_shader_parameter_end(main) +{ + const brx_float2 full_screen_triangle_positions[3] = brx_array_constructor_begin(brx_float2, 3) + brx_float2(-1.0, -1.0) brx_array_constructor_split + brx_float2(3.0, -1.0) brx_array_constructor_split + brx_float2(-1.0, 3.0) + brx_array_constructor_end; + + brx_position = brx_float4(full_screen_triangle_positions[brx_vertex_id], 0.5, 1.0); +} \ No newline at end of file diff --git a/shaders/gbuffer_fragment.sl b/shaders/gbuffer_fragment.sl new file mode 100644 index 0000000..35047c2 --- /dev/null +++ b/shaders/gbuffer_fragment.sl @@ -0,0 +1,112 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include "gbuffer_pipeline_resource_binding.sli" +#include "../thirdparty/ImportAsset/shaders/packed_vector.sli" +#include "../thirdparty/ImportAsset/shaders/octahedron_mapping.sli" + +brx_root_signature(gbuffer_root_signature_macro, gbuffer_root_signature_name) +brx_early_depth_stencil +brx_pixel_shader_parameter_begin(main) +brx_pixel_shader_parameter_in_frag_coord brx_pixel_shader_parameter_split +brx_pixel_shader_parameter_in(brx_float3, in_interpolated_normal, 0) brx_pixel_shader_parameter_split +brx_pixel_shader_parameter_in(brx_float4, in_interpolated_tangent, 1) brx_pixel_shader_parameter_split +brx_pixel_shader_parameter_in(brx_float2, in_interpolated_texcoord, 2) brx_pixel_shader_parameter_split +brx_pixel_shader_parameter_out(brx_uint4, out_gbuffer, 0) +brx_pixel_shader_parameter_end(main) +{ + brx_float3 geometry_normal_world_sapce = brx_normalize(in_interpolated_normal); + brx_float4 tangent_world_sapce = brx_float4(brx_normalize(in_interpolated_tangent.xyz), in_interpolated_tangent.w); + brx_float2 texcoord = in_interpolated_texcoord; + + brx_uint4 packed_material_buffer_vectors[3]; + packed_material_buffer_vectors[0] = brx_byte_address_buffer_load4(g_geometry_material_buffers[3], 0); + packed_material_buffer_vectors[1] = brx_byte_address_buffer_load4(g_geometry_material_buffers[3], 4 * 4); + packed_material_buffer_vectors[2].xy = brx_byte_address_buffer_load2(g_geometry_material_buffers[3], 4 * 4 + 4 * 4); + brx_uint geometry_material_flags = packed_material_buffer_vectors[0].x; + brx_float normal_texture_scale = brx_uint_as_float(packed_material_buffer_vectors[0].y); + brx_float3 emissive_factor = brx_uint_as_float(brx_uint3(packed_material_buffer_vectors[0].zw, packed_material_buffer_vectors[1].x)); + brx_float3 base_color_factor = brx_uint_as_float(packed_material_buffer_vectors[1].yzw); + brx_float metallic_factor = brx_uint_as_float(packed_material_buffer_vectors[2].x); + brx_float roughness_factor = brx_uint_as_float(packed_material_buffer_vectors[2].y); + + brx_float3 shading_normal_world_space; + brx_branch + if(0u != (geometry_material_flags & Material_Flag_Enable_Normal_Texture)) + { + // ["5.20.3. material.normalTextureInfo.scale" of "glTF 2.0 Specification"](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_material_normaltextureinfo_scale) + brx_float3 shading_normal_tangent_space = brx_normalize((brx_sample_2d(g_material_textures[0], g_sampler[0], texcoord).xyz * 2.0 - brx_float3(1.0, 1.0, 1.0)) * brx_float3(normal_texture_scale, normal_texture_scale, 1.0)); + brx_float3 bitangent_world_sapce = brx_cross(geometry_normal_world_sapce, tangent_world_sapce.xyz) * tangent_world_sapce.w; + shading_normal_world_space = brx_normalize(tangent_world_sapce.xyz * shading_normal_tangent_space.x + bitangent_world_sapce * shading_normal_tangent_space.y + geometry_normal_world_sapce * shading_normal_tangent_space.z); + } + else + { + shading_normal_world_space = geometry_normal_world_sapce; + } + + brx_float3 emissive = emissive_factor; + brx_branch + if(0u != (geometry_material_flags & Material_Flag_Enable_Emissive_Texture)) + { + emissive *= brx_sample_2d(g_material_textures[1], g_sampler[0], texcoord).xyz; + } + + brx_float3 base_color = base_color_factor; + brx_branch + if(0u != (geometry_material_flags & Material_Flag_Enable_Base_Colorl_Texture)) + { + base_color *= brx_sample_2d(g_material_textures[2], g_sampler[0], texcoord).xyz; + } + + brx_float metallic = metallic_factor; + brx_float roughness = roughness_factor; + brx_branch + if(0u != (geometry_material_flags & Material_Flag_Enable_Metallic_Roughness_Texture)) + { + brx_float2 metallic_roughness = brx_sample_2d(g_material_textures[3], g_sampler[0], texcoord).bg; + metallic *= metallic_roughness.x; + roughness *= metallic_roughness.y; + } + + // Specular Anti-Aliasing + // "7.8.1 Mipmapping BRDF and Normal Maps" of "Real-Time Rendering Third Edition" + // "9.13.1 Filtering Normals and Normal Distributions" of "Real-Time Rendering Fourth Edition" + // UE4: [NormalCurvatureToRoughness](https://github.com/EpicGames/UnrealEngine/blob/4.27/Engine/Shaders/Private/BasePassPixelShader.usf#L67) + // U3D: [TextureNormalVariance ](https://github.com/Unity-Technologies/Graphics/blob/v10.8.1/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl#L214) + { + const brx_float cvar_normal_curvature_to_roughness_scale = 1.0; + const brx_float cvar_normal_curvature_to_roughness_bias = 0.0; + const brx_float cvar_normal_curvature_to_roughness_exponent = 0.3333333; + + brx_float3 dn_dx = brx_ddx(geometry_normal_world_sapce); + brx_float3 dn_dy = brx_ddy(geometry_normal_world_sapce); + brx_float x = brx_dot(dn_dx, dn_dx); + brx_float y = brx_dot(dn_dy, dn_dy); + + brx_float curvature_approx = brx_pow(brx_max(x, y), cvar_normal_curvature_to_roughness_exponent); + brx_float geometric_aa_roughness = brx_clamp(curvature_approx * cvar_normal_curvature_to_roughness_scale + cvar_normal_curvature_to_roughness_bias, 0.0, 1.0); + + roughness = brx_max(roughness, geometric_aa_roughness); + } + + brx_uint packed_shading_normal_world_space = FLOAT2_to_R16G16_SNORM(octahedron_map(shading_normal_world_space)); + brx_uint packed_base_color = FLOAT4_to_R10G10B10A2_UNORM(brx_float4(base_color.x, base_color.y, base_color.z, 1.0)); + brx_uint packed_metallic_roughness = FLOAT2_to_R16G16_UNORM(brx_float2(metallic, roughness)); + brx_uint packed_emissive = FLOAT4_to_R10G10B10A2_UNORM(brx_float4(emissive.x, emissive.y, emissive.z, 1.0)); + + out_gbuffer = brx_uint4(packed_shading_normal_world_space, packed_base_color, packed_metallic_roughness, packed_emissive); +} \ No newline at end of file diff --git a/shaders/gbuffer_pipeline_resource_binding.sli b/shaders/gbuffer_pipeline_resource_binding.sli new file mode 100644 index 0000000..402091e --- /dev/null +++ b/shaders/gbuffer_pipeline_resource_binding.sli @@ -0,0 +1,70 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _GBUFFER_PIPELINE_RESOURCE_BINDING_SLI_ +#define _GBUFFER_PIPELINE_RESOURCE_BINDING_SLI_ 1 + +#include "../thirdparty/Brioche/shaders/brx_define.sli" +#include "common_resource_binding.sli" + +#define Geometry_Flag_Index_Type_UInt16 0x1u +#define Material_Flag_Enable_Normal_Texture 0x2u +#define Material_Flag_Enable_Emissive_Texture 0x4u +#define Material_Flag_Enable_Base_Colorl_Texture 0x8u +#define Material_Flag_Enable_Metallic_Roughness_Texture 0x10u + +#if defined(__cplusplus) +struct gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding +{ + uint32_t m_geometry_material_flags; + float m_normal_texture_scale; + float m_emissive_factor_x; + float m_emissive_factor_y; + + float m_emissive_factor_z; + float m_base_color_factor_x; + float m_base_color_factor_y; + float m_base_color_factor_z; + + float m_metallic_factor; + float m_roughness_factor; + uint32_t _unused_padding_1; + uint32_t _unused_padding_2; +}; +#endif + +brx_sampler_state(g_sampler, 0, 1, 1); + +brx_read_only_byte_address_buffer(g_geometry_material_buffers, 1, 0, 4); + +brx_texture_2d(g_material_textures, 1, 4, 4); + +brx_cbuffer(gbuffer_pipeline_per_mesh_instance_update_set_uniform_buffer_binding, 2, 0) +{ + brx_column_major brx_float4x4 g_model_transform; +}; + +#define gbuffer_root_signature_macro \ + brx_root_signature_root_parameter_begin(gbuffer_root_signature_name) \ + brx_root_signature_root_cbv(0, 0) brx_root_signature_root_parameter_split \ + brx_root_signature_root_descriptor_table_sampler(0, 1, 1) brx_root_signature_root_parameter_split \ + brx_root_signature_root_descriptor_table_srv(1, 0, 4) brx_root_signature_root_parameter_split \ + brx_root_signature_root_descriptor_table_srv(1, 4, 4) brx_root_signature_root_parameter_split \ + brx_root_signature_root_cbv(2, 0) \ + brx_root_signature_root_parameter_end + +#endif \ No newline at end of file diff --git a/shaders/gbuffer_vertex.sl b/shaders/gbuffer_vertex.sl new file mode 100644 index 0000000..d30c4d5 --- /dev/null +++ b/shaders/gbuffer_vertex.sl @@ -0,0 +1,72 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include "gbuffer_pipeline_resource_binding.sli" +#include "../thirdparty/ImportAsset/shaders/packed_vector.sli" +#include "../thirdparty/ImportAsset/shaders/octahedron_mapping.sli" +#include "common_constant.sli" + +brx_root_signature(gbuffer_root_signature_macro, gbuffer_root_signature_name) +brx_vertex_shader_parameter_begin(main) +brx_vertex_shader_parameter_in_vertex_id brx_vertex_shader_parameter_split +brx_vertex_shader_parameter_out_position brx_vertex_shader_parameter_split +brx_vertex_shader_parameter_out(brx_float3, out_vertex_normal, 0) brx_vertex_shader_parameter_split +brx_vertex_shader_parameter_out(brx_float4, out_vertex_tagent, 1) brx_vertex_shader_parameter_split +brx_vertex_shader_parameter_out(brx_float2, out_vertex_texcoord, 2) +brx_vertex_shader_parameter_end(main) +{ + brx_uint geometry_material_flags = brx_byte_address_buffer_load(g_geometry_material_buffers[3], 0); + + brx_uint vertex_index; + brx_branch + if(0u != (geometry_material_flags & Geometry_Flag_Index_Type_UInt16)) + { + brx_uint index_buffer_offset = (0u == ((g_index_uint16_buffer_stride * brx_uint(brx_vertex_id)) % 4u)) ? (g_index_uint16_buffer_stride * brx_uint(brx_vertex_id)) : ((g_index_uint16_buffer_stride * brx_uint(brx_vertex_id)) - 2u); + brx_uint packed_vector_index_buffer = brx_byte_address_buffer_load(g_geometry_material_buffers[2], index_buffer_offset); + brx_uint2 unpacked_vector_index_buffer = R16G16_UINT_to_UINT2(packed_vector_index_buffer); + vertex_index = (0u == ((g_index_uint16_buffer_stride * brx_uint(brx_vertex_id)) % 4u)) ? unpacked_vector_index_buffer.x : unpacked_vector_index_buffer.y; + } + else + { + brx_uint index_buffer_offset = g_index_uint32_buffer_stride * brx_uint(brx_vertex_id); + vertex_index = brx_byte_address_buffer_load(g_geometry_material_buffers[2], index_buffer_offset); + } + + brx_uint vertex_position_buffer_offset = g_vertex_position_buffer_stride * vertex_index; + brx_uint3 packed_vector_vertex_position_buffer = brx_byte_address_buffer_load3(g_geometry_material_buffers[0], vertex_position_buffer_offset); + brx_float3 vertex_position_model_space = brx_uint_as_float(packed_vector_vertex_position_buffer); + + brx_uint vertex_varying_buffer_offset = g_vertex_varying_buffer_stride * vertex_index; + brx_uint3 packed_vector_vertex_varying_buffer = brx_byte_address_buffer_load3(g_geometry_material_buffers[1], vertex_varying_buffer_offset); + brx_float3 vertex_normal_model_space = octahedron_unmap(R16G16_SNORM_to_FLOAT2(packed_vector_vertex_varying_buffer.x)); + brx_float3 vertex_mapped_tangent_model_space = R15G15B2_SNORM_to_FLOAT3(packed_vector_vertex_varying_buffer.y); + brx_float4 vertex_tangent_model_space = brx_float4(octahedron_unmap(vertex_mapped_tangent_model_space.xy), vertex_mapped_tangent_model_space.z); + brx_float2 vertex_texcoord = R16G16_UNORM_to_FLOAT2(packed_vector_vertex_varying_buffer.z); + + brx_float3 vertex_position_world_space = brx_mul(g_model_transform, brx_float4(vertex_position_model_space, 1.0)).xyz; + brx_float3 vertex_position_view_space = brx_mul(g_view_transform, brx_float4(vertex_position_world_space, 1.0)).xyz; + brx_float4 vertex_position_clip_space = brx_mul(g_projection_transform, brx_float4(vertex_position_view_space, 1.0)); + + brx_float3 vertex_normal_world_space = brx_mul(g_model_transform, brx_float4(vertex_normal_model_space, 0.0)).xyz; + + brx_float4 vertex_tangent_world_space = brx_float4(brx_mul(g_model_transform, brx_float4(vertex_tangent_model_space.xyz, 0.0)).xyz, vertex_tangent_model_space.w); + + brx_position = vertex_position_clip_space; + out_vertex_normal = vertex_normal_world_space; + out_vertex_tagent = vertex_tangent_world_space; + out_vertex_texcoord = vertex_texcoord; +} diff --git a/shaders/math_constant.sli b/shaders/math_constant.sli new file mode 100644 index 0000000..dbdbb79 --- /dev/null +++ b/shaders/math_constant.sli @@ -0,0 +1,23 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _MATH_CONSTANT_SLI_ +#define _MATH_CONSTANT_SLI_ 1 + +#define M_PI 3.141592653589793238462643 + +#endif \ No newline at end of file diff --git a/shaders/skin_compute.sl b/shaders/skin_compute.sl new file mode 100644 index 0000000..f418e45 --- /dev/null +++ b/shaders/skin_compute.sl @@ -0,0 +1,80 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include "skin_pipeline_resource_binding.sli" +#include "../thirdparty/ImportAsset/shaders/packed_vector.sli" +#include "../thirdparty/ImportAsset/shaders/octahedron_mapping.sli" +#include "common_constant.sli" + +#if defined(GL_SPIRV) || defined(VULKAN) +#define brx_dual_quaternion mat2x4 +#include "../thirdparty/DLB/DLB.glsli" +#elif defined(HLSL_VERSION) || defined(__HLSL_VERSION) +#define brx_dual_quaternion float2x4 +#include "../thirdparty/DLB/DLB.hlsli" +#else +#error Unknown Compiler +#endif + +#define THREAD_GROUP_X 1 +#define THREAD_GROUP_Y 1 +#define THREAD_GROUP_Z 1 + +brx_root_signature(skin_root_signature_macro, skin_root_signature_name) +brx_num_threads(THREAD_GROUP_X, THREAD_GROUP_Y, THREAD_GROUP_Z) +brx_compute_shader_parameter_begin(main) +brx_compute_shader_parameter_in_group_id +brx_pixel_shader_parameter_end(main) +{ + brx_uint vertex_index = brx_group_id.x; + + brx_uint vertex_position_buffer_offset = g_vertex_position_buffer_stride * vertex_index; + brx_uint3 packed_vector_vertex_position_buffer = brx_byte_address_buffer_load3(g_geometry_buffers[0], vertex_position_buffer_offset); + brx_float3 vertex_position_model_space = brx_uint_as_float(packed_vector_vertex_position_buffer); + + brx_uint vertex_varying_buffer_offset = g_vertex_varying_buffer_stride * vertex_index; + brx_uint3 packed_vector_vertex_varying_buffer = brx_byte_address_buffer_load3(g_geometry_buffers[1], vertex_varying_buffer_offset); + brx_float3 vertex_normal_model_space = octahedron_unmap(R16G16_SNORM_to_FLOAT2(packed_vector_vertex_varying_buffer.x)); + brx_float3 vertex_mapped_tangent_model_space = R15G15B2_SNORM_to_FLOAT3(packed_vector_vertex_varying_buffer.y); + brx_float4 vertex_tangent_model_space = brx_float4(octahedron_unmap(vertex_mapped_tangent_model_space.xy), vertex_mapped_tangent_model_space.z); + + brx_uint vertex_joint_buffer_offset = g_vertex_joint_buffer_stride * vertex_index; + brx_uint3 packed_vector_vertex_joint_buffer = brx_byte_address_buffer_load3(g_geometry_buffers[2], vertex_joint_buffer_offset); + + brx_uint4 joint_indices = R16G16B16A16_UINT_to_UINT4(packed_vector_vertex_joint_buffer.xy); + brx_float4 joint_weights = R8G8B8A8_UNORM_to_FLOAT4(packed_vector_vertex_joint_buffer.z); + + brx_dual_quaternion dual_quaternion_indices_x = brx_dual_quaternion(g_dual_quaternions[2u * joint_indices.x], g_dual_quaternions[2u * joint_indices.x + 1u]); + brx_dual_quaternion dual_quaternion_indices_y = brx_dual_quaternion(g_dual_quaternions[2u * joint_indices.y], g_dual_quaternions[2u * joint_indices.y + 1u]); + brx_dual_quaternion dual_quaternion_indices_z = brx_dual_quaternion(g_dual_quaternions[2u * joint_indices.z], g_dual_quaternions[2u * joint_indices.z + 1u]); + brx_dual_quaternion dual_quaternion_indices_w = brx_dual_quaternion(g_dual_quaternions[2u * joint_indices.w], g_dual_quaternions[2u * joint_indices.w + 1u]); + + brx_dual_quaternion blend_dual_quaternion = dual_quaternion_linear_blending(dual_quaternion_indices_x, dual_quaternion_indices_y, dual_quaternion_indices_z, dual_quaternion_indices_w, joint_weights); + + brx_float4 blend_quaternion; + brx_float3 blend_translation; + unit_dual_quaternion_to_rigid_transform(blend_dual_quaternion, blend_quaternion, blend_translation); + + brx_float3 skined_vertex_position_model_space = unit_quaternion_to_rotation_transform(blend_quaternion, vertex_position_model_space) + blend_translation; + brx_uint3 packed_vector_skined_vertex_position_buffer = brx_float_as_uint(skined_vertex_position_model_space); + brx_byte_address_buffer_store3(g_skinned_geometry_buffers[0], vertex_position_buffer_offset, packed_vector_skined_vertex_position_buffer); + + brx_float3 skined_vertex_normal_model_space = unit_quaternion_to_rotation_transform(blend_quaternion, vertex_normal_model_space); + brx_float4 skined_vertex_tangent_model_space = brx_float4(unit_quaternion_to_rotation_transform(blend_quaternion, vertex_tangent_model_space.xyz), vertex_tangent_model_space.w); + brx_uint3 packed_vector_skined_vertex_varying_buffer = brx_uint3(FLOAT2_to_R16G16_SNORM(octahedron_map(skined_vertex_normal_model_space)), FLOAT3_to_R15G15B2_SNORM(brx_float3(octahedron_map(skined_vertex_tangent_model_space.xyz), skined_vertex_tangent_model_space.w)), packed_vector_vertex_varying_buffer.z); + brx_byte_address_buffer_store3(g_skinned_geometry_buffers[1], vertex_varying_buffer_offset, packed_vector_skined_vertex_varying_buffer); +} \ No newline at end of file diff --git a/shaders/skin_pipeline_resource_binding.sli b/shaders/skin_pipeline_resource_binding.sli new file mode 100644 index 0000000..2fed8b4 --- /dev/null +++ b/shaders/skin_pipeline_resource_binding.sli @@ -0,0 +1,45 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _SKIN_PIPELINE_RESOURCE_BINDING_SLI_ +#define _SKIN_PIPELINE_RESOURCE_BINDING_SLI_ 1 + +#include "../thirdparty/Brioche/shaders/brx_define.sli" + +// D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT 4096 +// Vulkan Core: maxUniformBufferRange 16384 = 16 * 1024 +// Vulkan Roadmap 2022: maxUniformBufferRange 65536 = 16 * 4096 + +#define MAX_JOINT_COUNT 2048 + +brx_cbuffer(skin_pipeline_per_mesh_instance_update_set_uniform_buffer_binding, 0, 0) +{ + brx_column_major brx_float4 g_dual_quaternions[2 * MAX_JOINT_COUNT]; +}; + +brx_read_only_byte_address_buffer(g_geometry_buffers, 1, 0, 3); + +brx_write_only_byte_address_buffer(g_skinned_geometry_buffers, 1, 1, 2); + +#define skin_root_signature_macro \ + brx_root_signature_root_parameter_begin(skin_root_signature_name) \ + brx_root_signature_root_cbv(0, 0) brx_root_signature_root_parameter_split \ + brx_root_signature_root_descriptor_table_srv(1, 0, 3) brx_root_signature_root_parameter_split \ + brx_root_signature_root_descriptor_table_uav(1, 1, 2) \ + brx_root_signature_root_parameter_end + +#endif \ No newline at end of file diff --git a/shaders/support/full_screen_transfer_fragment.sl b/shaders/support/full_screen_transfer_fragment.sl new file mode 100644 index 0000000..d2cdb51 --- /dev/null +++ b/shaders/support/full_screen_transfer_fragment.sl @@ -0,0 +1,45 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include "full_screen_transfer_pipeline_layout.sli" + +brx_root_signature(full_screen_transfer_root_signature_macro, full_screen_transfer_root_signature_name) +brx_early_depth_stencil +brx_pixel_shader_parameter_begin(main) +brx_pixel_shader_parameter_in_frag_coord brx_pixel_shader_parameter_split +brx_pixel_shader_parameter_out(brx_float4, out_image, 0) +brx_pixel_shader_parameter_end(main) +{ + // TODO: HDR swapchain + // + // DXGI_COLOR_SPACE_TYPE: https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range#setting-up-your-directx-swap-chain + // Direct3D 12 HDR sample: https://learn.microsoft.com/en-us/samples/microsoft/directx-graphics-samples/d3d12-hdr-sample-win32/ + // VK_EXT_swapchain_colorspace: https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html#TRANSFER_CONVERSION + // https://developer.nvidia.com/high-dynamic-range-display-development + // https://gpuopen.com/learn/using-amd-freesync-2-hdr-color-spaces/ + // + // Options | hardware OETF input | hardware OETF output | Direct3D12 | Vulkan + // sRGB | sRGB | Bt709 | DXGI_FORMAT_R8G8B8A8_UNORM + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 | VK_FORMAT_B8G8R8A8_UNORM + VK_COLOR_SPACE_BT709_NONLINEAR_EXT + // sRGB | sRGB | Bt709 | DXGI_FORMAT_R10G10B10A2_UNORM + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 | VK_FORMAT_A2B10G10R10_UNORM_PACK32 + VK_COLOR_SPACE_BT709_NONLINEAR_EXT + // HDR10 | ST2084 | Bt2020 | DXGI_FORMAT_R10G10B10A2_UNORM + DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 | VK_FORMAT_A2B10G10R10_UNORM_PACK32 + VK_COLOR_SPACE_HDR10_ST2084_EXT + // scRGB | scRGB | Bt709 | N/A | VK_FORMAT_R16G16B16A16_SFLOAT + VK_COLOR_SPACE_BT709_LINEAR_EXT + // Linear BT2020 | Linear BT2020 | Bt2020 | DXGI_FORMAT_R16G16B16A16_FLOAT + DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 | VK_FORMAT_R16G16B16A16_SFLOAT + VK_COLOR_SPACE_BT2020_LINEAR_EXT + + brx_float3 color_linear = brx_load_2d(g_in_texture[0], brx_int3(brx_frag_coord.xy, 0)).xyz; + brx_float3 color_srgb = brx_float3(brx_pow(brx_clamp(color_linear.x, 0.0, 1.0), (1.0 / 2.2)), brx_pow(brx_clamp(color_linear.y, 0.0, 1.0), (1.0 / 2.2)), brx_pow(brx_clamp(color_linear.z, 0.0, 1.0), (1.0 / 2.2))); + out_image = brx_float4(color_srgb, 1.0); +} \ No newline at end of file diff --git a/shaders/support/full_screen_transfer_pipeline_layout.sli b/shaders/support/full_screen_transfer_pipeline_layout.sli new file mode 100644 index 0000000..b618702 --- /dev/null +++ b/shaders/support/full_screen_transfer_pipeline_layout.sli @@ -0,0 +1,32 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _FULL_SCREEN_TRANSFER_PIPELINE_LAYOUT_SLI_ +#define _FULL_SCREEN_TRANSFER_PIPELINE_LAYOUT_SLI_ 1 + +#include "../../thirdparty/Brioche/shaders/brx_define.sli" + +brx_texture_2d(g_in_texture, 0, 0, 1); + +brx_sampler_state(g_sampler, 0, 1, 1); + +#define full_screen_transfer_root_signature_macro \ + brx_root_signature_root_parameter_begin(full_screen_transfer_root_signature_name) \ + brx_root_signature_root_descriptor_table_srv(0, 0, 1) brx_root_signature_root_parameter_split \ + brx_root_signature_root_parameter_end + +#endif \ No newline at end of file diff --git a/shaders/support/full_screen_transfer_vertex.sl b/shaders/support/full_screen_transfer_vertex.sl new file mode 100644 index 0000000..1bfb9bc --- /dev/null +++ b/shaders/support/full_screen_transfer_vertex.sl @@ -0,0 +1,33 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include "full_screen_transfer_pipeline_layout.sli" + +brx_root_signature(full_screen_transfer_root_signature_macro, full_screen_transfer_root_signature_name) +brx_vertex_shader_parameter_begin(main) +brx_vertex_shader_parameter_in_vertex_id brx_vertex_shader_parameter_split +brx_vertex_shader_parameter_out_position +brx_vertex_shader_parameter_end(main) +{ + const brx_float2 full_screen_triangle_positions[3] = brx_array_constructor_begin(brx_float2, 3) + brx_float2(-1.0, -1.0) brx_array_constructor_split + brx_float2(3.0, -1.0) brx_array_constructor_split + brx_float2(-1.0, 3.0) + brx_array_constructor_end; + + brx_position = brx_float4(full_screen_triangle_positions[brx_vertex_id], 0.5, 1.0); +} \ No newline at end of file diff --git a/source/demo.cpp b/source/demo.cpp new file mode 100644 index 0000000..95d8fa9 --- /dev/null +++ b/source/demo.cpp @@ -0,0 +1,1449 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include "demo.h" +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#endif +#include +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +#include +#include +#include +#include +#include +#include "support/camera_controller.h" +#include "../thirdparty/DLB/DLB.h" +#include "../thirdparty/ImportAsset/include/import_image_asset.h" +#include "../thirdparty/ImportAsset/include/import_asset_input_stream.h" +#include "../thirdparty/ImportAsset/shaders/octahedron_mapping.sli" +#include "../shaders/common_constant.sli" +#include "../shaders/skin_pipeline_resource_binding.sli" +#include "../shaders/common_resource_binding.sli" +#include "../shaders/gbuffer_pipeline_resource_binding.sli" +#include "../shaders/deferred_shading_pipeline_resource_binding.sli" + +static inline uint32_t tbb_align_up(uint32_t value, uint32_t alignment); + +static inline DirectX::XMMATRIX XM_CALLCONV DirectX_Math_Matrix_PerspectiveFovRH_ReversedZ(float FovAngleY, float AspectRatio, float NearZ, float FarZ); + +static float g_camera_fov = -1.0F; + +Demo::Demo() +{ +} + +void Demo::init(brx_device *device, std::vector const &file_names) +{ + // Descriptor Layout + brx_descriptor_set_layout *skin_pipeline_per_mesh_instance_update_descriptor_set_layout; + brx_descriptor_set_layout *skin_pipeline_per_mesh_skinned_subset_update_descriptor_set_layout; + brx_descriptor_set_layout *gbuffer_pipeline_none_update_descriptor_set_layout; + brx_descriptor_set_layout *gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout; + brx_descriptor_set_layout *gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout; + { + BRX_DESCRIPTOR_SET_LAYOUT_BINDING const skin_pipeline_per_mesh_instance_update_descriptor_set_layout_bindings[] = { + {0U, BRX_DESCRIPTOR_TYPE_DYNAMIC_UNIFORM_BUFFER, 1U}}; + skin_pipeline_per_mesh_instance_update_descriptor_set_layout = device->create_descriptor_set_layout(sizeof(skin_pipeline_per_mesh_instance_update_descriptor_set_layout_bindings) / sizeof(skin_pipeline_per_mesh_instance_update_descriptor_set_layout_bindings[0]), skin_pipeline_per_mesh_instance_update_descriptor_set_layout_bindings); + + BRX_DESCRIPTOR_SET_LAYOUT_BINDING const skin_pipeline_per_mesh_skined_subset_update_descriptor_set_layout_bindings[] = { + {0U, BRX_DESCRIPTOR_TYPE_READ_ONLY_STORAGE_BUFFER, 3U}, + {1U, BRX_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2U}}; + skin_pipeline_per_mesh_skinned_subset_update_descriptor_set_layout = device->create_descriptor_set_layout(sizeof(skin_pipeline_per_mesh_skined_subset_update_descriptor_set_layout_bindings) / sizeof(skin_pipeline_per_mesh_skined_subset_update_descriptor_set_layout_bindings[0]), skin_pipeline_per_mesh_skined_subset_update_descriptor_set_layout_bindings); + + brx_descriptor_set_layout *const skin_pipeline_descriptor_set_layouts[] = { + skin_pipeline_per_mesh_instance_update_descriptor_set_layout, skin_pipeline_per_mesh_skinned_subset_update_descriptor_set_layout}; + this->m_skin_pipeline_layout = device->create_pipeline_layout(sizeof(skin_pipeline_descriptor_set_layouts) / sizeof(skin_pipeline_descriptor_set_layouts[0]), skin_pipeline_descriptor_set_layouts); + + BRX_DESCRIPTOR_SET_LAYOUT_BINDING const gbuffer_pipeline_none_update_descriptor_set_layout_bindings[] = { + {0U, BRX_DESCRIPTOR_TYPE_DYNAMIC_UNIFORM_BUFFER, 1U}, + {1U, BRX_DESCRIPTOR_TYPE_SAMPLER, 1U}}; + gbuffer_pipeline_none_update_descriptor_set_layout = device->create_descriptor_set_layout(sizeof(gbuffer_pipeline_none_update_descriptor_set_layout_bindings) / sizeof(gbuffer_pipeline_none_update_descriptor_set_layout_bindings[0]), gbuffer_pipeline_none_update_descriptor_set_layout_bindings); + + BRX_DESCRIPTOR_SET_LAYOUT_BINDING const gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout_bindings[] = { + {0U, BRX_DESCRIPTOR_TYPE_READ_ONLY_STORAGE_BUFFER, 4U}, + {4U, BRX_DESCRIPTOR_TYPE_SAMPLED_IMAGE, DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT}}; + gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout = device->create_descriptor_set_layout(sizeof(gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout_bindings) / sizeof(gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout_bindings[0]), gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout_bindings); + + BRX_DESCRIPTOR_SET_LAYOUT_BINDING const gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout_bindings[] = { + {0U, BRX_DESCRIPTOR_TYPE_DYNAMIC_UNIFORM_BUFFER, 1U}}; + gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout = device->create_descriptor_set_layout(sizeof(gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout_bindings) / sizeof(gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout_bindings[0]), gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout_bindings); + + brx_descriptor_set_layout *const gbuffer_pipeline_descriptor_set_layouts[] = { + gbuffer_pipeline_none_update_descriptor_set_layout, gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout, gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout}; + this->m_gbuffer_pipeline_layout = device->create_pipeline_layout(sizeof(gbuffer_pipeline_descriptor_set_layouts) / sizeof(gbuffer_pipeline_descriptor_set_layouts[0]), gbuffer_pipeline_descriptor_set_layouts); + + BRX_DESCRIPTOR_SET_LAYOUT_BINDING const deferred_shading_pipeline_none_update_descriptor_set_layout_bindings[] = { + {0U, BRX_DESCRIPTOR_TYPE_DYNAMIC_UNIFORM_BUFFER, 1U}, + {1U, BRX_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1U}, + {2U, BRX_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1U}}; + this->m_deferred_shading_pipeline_none_update_descriptor_set_layout = device->create_descriptor_set_layout(sizeof(deferred_shading_pipeline_none_update_descriptor_set_layout_bindings) / sizeof(deferred_shading_pipeline_none_update_descriptor_set_layout_bindings[0]), deferred_shading_pipeline_none_update_descriptor_set_layout_bindings); + + brx_descriptor_set_layout *const deferred_shading_pipeline_descriptor_set_layouts[] = { + this->m_deferred_shading_pipeline_none_update_descriptor_set_layout}; + this->m_deferred_shading_pipeline_layout = device->create_pipeline_layout(sizeof(deferred_shading_pipeline_descriptor_set_layouts) / sizeof(deferred_shading_pipeline_descriptor_set_layouts[0]), deferred_shading_pipeline_descriptor_set_layouts); + } + + // Render Pass and Pipeline + { + // Skin Pipeline + { +#include + this->m_skin_pipeline = device->create_compute_pipeline(this->m_skin_pipeline_layout, sizeof(skin_compute_shader_module_code), skin_compute_shader_module_code); + } + + // GBuffer Render Pass + { + this->m_gbuffer_color_attachement_format = BRX_COLOR_ATTACHMENT_FORMAT_R32G32B32A32_UINT; + this->m_gbuffer_depth_attachement_format = device->get_depth_attachment_image_format(); + + BRX_RENDER_PASS_COLOR_ATTACHMENT const color_attachment = { + this->m_gbuffer_color_attachement_format, + BRX_RENDER_PASS_COLOR_ATTACHMENT_LOAD_OPERATION_CLEAR, + BRX_RENDER_PASS_COLOR_ATTACHMENT_STORE_OPERATION_FLUSH_FOR_SAMPLED_IMAGE}; + + BRX_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT const depth_stencil_attachment = { + this->m_gbuffer_depth_attachement_format, + BRX_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_LOAD_OPERATION_CLEAR, + BRX_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_STORE_OPERATION_FLUSH_FOR_SAMPLED_IMAGE}; + + this->m_gbuffer_render_pass = device->create_render_pass(1U, &color_attachment, &depth_stencil_attachment); + } + + // GBuffer Pipeline + { +#include +#include + this->m_gbuffer_pipeline = device->create_graphics_pipeline(this->m_gbuffer_render_pass, this->m_gbuffer_pipeline_layout, sizeof(gbuffer_vertex_shader_module_code), gbuffer_vertex_shader_module_code, sizeof(gbuffer_fragment_shader_module_code), gbuffer_fragment_shader_module_code, true, BRX_GRAPHICS_PIPELINE_COMPARE_OPERATION_GREATER); + } + + // Deferred Shading Pass + { + this->m_deferred_shading_color_attachement_format = BRX_COLOR_ATTACHMENT_FORMAT_A2R10G10B10_UNORM_PACK32; + + BRX_RENDER_PASS_COLOR_ATTACHMENT const color_attachment = { + this->m_deferred_shading_color_attachement_format, + BRX_RENDER_PASS_COLOR_ATTACHMENT_LOAD_OPERATION_CLEAR, + BRX_RENDER_PASS_COLOR_ATTACHMENT_STORE_OPERATION_FLUSH_FOR_SAMPLED_IMAGE}; + + this->m_deferred_shading_render_pass = device->create_render_pass(1U, &color_attachment, NULL); + } + + // Deferred Shading Pipeline + { +#include +#include + this->m_deferred_shading_pipeline = device->create_graphics_pipeline(this->m_deferred_shading_render_pass, this->m_deferred_shading_pipeline_layout, sizeof(deferred_shading_vertex_shader_module_code), deferred_shading_vertex_shader_module_code, sizeof(deferred_shading_fragment_shader_module_code), deferred_shading_fragment_shader_module_code, false, BRX_GRAPHICS_PIPELINE_COMPARE_OPERATION_ALWAYS); + } + } + + // Asset & Place Holder Texture + { + brx_upload_command_buffer *const upload_command_buffer = device->create_upload_command_buffer(); + + brx_graphics_command_buffer *const graphics_command_buffer = device->create_graphics_command_buffer(); + + brx_upload_queue *const upload_queue = device->create_upload_queue(); + + brx_graphics_queue *const graphics_queue = device->create_graphics_queue(); + + brx_fence *const fence = device->create_fence(true); + + { + std::vector staging_upload_buffers; + + device->reset_upload_command_buffer(upload_command_buffer); + + device->reset_graphics_command_buffer(graphics_command_buffer); + + upload_command_buffer->begin(); + + graphics_command_buffer->begin(); + + import_asset_input_stream_factory *input_stream_factory = import_asset_init_file_input_stream_factory(); + + // 60 FPS + constexpr float const frame_rate = 60.0F; + + // Asset + for (size_t file_name_index = 0U; file_name_index < file_names.size(); ++file_name_index) + { + std::string const &file_name = file_names[file_name_index]; + + std::vector total_mesh_data; + if (import_gltf_scene_asset(total_mesh_data, frame_rate, input_stream_factory, file_name.c_str())) + { + size_t mesh_index_offset = this->m_scene_meshes.size(); + + this->m_scene_meshes.resize(mesh_index_offset + total_mesh_data.size()); + + for (size_t mesh_index = 0U; mesh_index < total_mesh_data.size(); ++mesh_index) + { + scene_mesh_data const &in_mesh_data = total_mesh_data[mesh_index]; + + Demo_Mesh &out_mesh = this->m_scene_meshes[mesh_index_offset + mesh_index]; + + out_mesh.m_skinned = in_mesh_data.m_skinned; + + out_mesh.m_subsets.resize(in_mesh_data.m_subsets.size()); + + for (size_t subset_index = 0U; subset_index < in_mesh_data.m_subsets.size(); ++subset_index) + { + scene_mesh_subset_data const &in_subset_data = in_mesh_data.m_subsets[subset_index]; + + Demo_Mesh_Subset &out_subset = out_mesh.m_subsets[subset_index]; + + out_subset.m_vertex_count = static_cast(in_subset_data.m_vertex_position_binding.size()); + + { + uint32_t const vertex_position_buffer_size = sizeof(scene_mesh_vertex_position_binding) * out_subset.m_vertex_count; + + out_subset.m_vertex_position_buffer = device->create_storage_asset_buffer(vertex_position_buffer_size); + + brx_staging_upload_buffer *const vertex_position_staging_upload_buffer = device->create_staging_upload_buffer(vertex_position_buffer_size); + + staging_upload_buffers.push_back(vertex_position_staging_upload_buffer); + + std::memcpy(vertex_position_staging_upload_buffer->get_host_memory_range_base(), in_subset_data.m_vertex_position_binding.data(), vertex_position_buffer_size); + + upload_command_buffer->upload_from_staging_upload_buffer_to_storage_asset_buffer(out_subset.m_vertex_position_buffer, 0U, vertex_position_staging_upload_buffer, 0U, vertex_position_buffer_size); + } + + { + assert(out_subset.m_vertex_count == in_subset_data.m_vertex_varying_binding.size()); + + uint32_t const vertex_varying_buffer_size = sizeof(scene_mesh_vertex_varying_binding) * out_subset.m_vertex_count; + + out_subset.m_vertex_varying_buffer = device->create_storage_asset_buffer(vertex_varying_buffer_size); + + brx_staging_upload_buffer *const vertex_varying_staging_upload_buffer = device->create_staging_upload_buffer(vertex_varying_buffer_size); + + staging_upload_buffers.push_back(vertex_varying_staging_upload_buffer); + + std::memcpy(vertex_varying_staging_upload_buffer->get_host_memory_range_base(), in_subset_data.m_vertex_varying_binding.data(), vertex_varying_buffer_size); + + upload_command_buffer->upload_from_staging_upload_buffer_to_storage_asset_buffer(out_subset.m_vertex_varying_buffer, 0U, vertex_varying_staging_upload_buffer, 0U, vertex_varying_buffer_size); + } + + if (!in_mesh_data.m_skinned) + { + out_subset.m_vertex_joint_buffer = NULL; + } + else + { + assert(out_subset.m_vertex_count == in_subset_data.m_vertex_joint_binding.size()); + + uint32_t const vertex_joint_buffer_size = sizeof(scene_mesh_vertex_joint_binding) * out_subset.m_vertex_count; + + out_subset.m_vertex_joint_buffer = device->create_storage_asset_buffer(vertex_joint_buffer_size); + + brx_staging_upload_buffer *const vertex_joint_staging_upload_buffer = device->create_staging_upload_buffer(vertex_joint_buffer_size); + + staging_upload_buffers.push_back(vertex_joint_staging_upload_buffer); + + std::memcpy(vertex_joint_staging_upload_buffer->get_host_memory_range_base(), in_subset_data.m_vertex_joint_binding.data(), vertex_joint_buffer_size); + + upload_command_buffer->upload_from_staging_upload_buffer_to_storage_asset_buffer(out_subset.m_vertex_joint_buffer, 0U, vertex_joint_staging_upload_buffer, 0U, vertex_joint_buffer_size); + } + + out_subset.m_index_count = static_cast(in_subset_data.m_indices.size()); + + { + if (in_subset_data.m_max_index <= static_cast(UINT16_MAX)) + { + std::vector converted_indices(static_cast(out_subset.m_index_count)); + for (uint32_t index_index = 0; index_index < out_subset.m_index_count; ++index_index) + { + converted_indices[index_index] = static_cast(in_subset_data.m_indices[index_index]); + } + + uint32_t const index_buffer_size = sizeof(uint16_t) * out_subset.m_index_count; + + out_subset.m_index_buffer = device->create_storage_asset_buffer(index_buffer_size); + + brx_staging_upload_buffer *const index_staging_upload_buffer = device->create_staging_upload_buffer(index_buffer_size); + + staging_upload_buffers.push_back(index_staging_upload_buffer); + + std::memcpy(index_staging_upload_buffer->get_host_memory_range_base(), converted_indices.data(), index_buffer_size); + + upload_command_buffer->upload_from_staging_upload_buffer_to_storage_asset_buffer(out_subset.m_index_buffer, 0U, index_staging_upload_buffer, 0U, index_buffer_size); + } + else + { + uint32_t const index_buffer_size = sizeof(uint32_t) * out_subset.m_index_count; + + out_subset.m_index_buffer = device->create_storage_asset_buffer(index_buffer_size); + + brx_staging_upload_buffer *const index_staging_upload_buffer = device->create_staging_upload_buffer(index_buffer_size); + + staging_upload_buffers.push_back(index_staging_upload_buffer); + + std::memcpy(index_staging_upload_buffer->get_host_memory_range_base(), in_subset_data.m_indices.data(), index_buffer_size); + + upload_command_buffer->upload_from_staging_upload_buffer_to_storage_asset_buffer(out_subset.m_index_buffer, 0U, index_staging_upload_buffer, 0U, index_buffer_size); + } + } + + { + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination; + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_geometry_material_flags = 0U; + if (in_subset_data.m_max_index <= static_cast(UINT16_MAX)) + { + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_geometry_material_flags |= Geometry_Flag_Index_Type_UInt16; + } + if (!in_subset_data.m_normal_texture_image_uri.empty()) + { + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_geometry_material_flags |= Material_Flag_Enable_Normal_Texture; + } + if (!in_subset_data.m_emissive_texture_image_uri.empty()) + { + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_geometry_material_flags |= Material_Flag_Enable_Emissive_Texture; + } + if (!in_subset_data.m_base_color_texture_image_uri.empty()) + { + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_geometry_material_flags |= Material_Flag_Enable_Base_Colorl_Texture; + } + if (!in_subset_data.m_metallic_roughness_texture_image_uri.empty()) + { + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_geometry_material_flags |= Material_Flag_Enable_Metallic_Roughness_Texture; + } + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_normal_texture_scale = in_subset_data.m_normal_texture_scale; + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_emissive_factor_x = in_subset_data.m_emissive_factor.x; + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_emissive_factor_y = in_subset_data.m_emissive_factor.y; + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_emissive_factor_z = in_subset_data.m_emissive_factor.z; + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_base_color_factor_x = in_subset_data.m_base_color_factor.x; + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_base_color_factor_y = in_subset_data.m_base_color_factor.y; + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_base_color_factor_z = in_subset_data.m_base_color_factor.z; + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_metallic_factor = in_subset_data.m_metallic_factor; + gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination.m_roughness_factor = in_subset_data.m_roughness_factor; + + uint32_t const material_buffer_size = sizeof(gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination); + + out_subset.m_material_buffer = device->create_storage_asset_buffer(material_buffer_size); + + brx_staging_upload_buffer *const material_staging_upload_buffer = device->create_staging_upload_buffer(material_buffer_size); + + staging_upload_buffers.push_back(material_staging_upload_buffer); + + std::memcpy(material_staging_upload_buffer->get_host_memory_range_base(), &gbuffer_pipeline_per_mesh_subset_update_set_geometry_material_storage_buffer_binding_destination, material_buffer_size); + + upload_command_buffer->upload_from_staging_upload_buffer_to_storage_asset_buffer(out_subset.m_material_buffer, 0U, material_staging_upload_buffer, 0U, material_buffer_size); + } + + { + uint32_t const staging_upload_buffer_offset_alignment = device->get_staging_upload_buffer_offset_alignment(); + uint32_t const staging_upload_buffer_row_pitch_alignment = device->get_staging_upload_buffer_row_pitch_alignment(); + + std::string const *const meterial_texture_image_uris[] = { + &in_subset_data.m_normal_texture_image_uri, + &in_subset_data.m_emissive_texture_image_uri, + &in_subset_data.m_base_color_texture_image_uri, + &in_subset_data.m_metallic_roughness_texture_image_uri}; + + for (uint32_t mesh_subset_meterial_texture_index = 0U; mesh_subset_meterial_texture_index < DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT; ++mesh_subset_meterial_texture_index) + { + std::string const &meterial_texture_image_uri = (*meterial_texture_image_uris[mesh_subset_meterial_texture_index]); + + if (!meterial_texture_image_uri.empty()) + { + std::string image_asset_file_name_dds; + std::string image_asset_file_name_pvr; + { + std::string image_asset_file_name; + + size_t dir_name_pos = file_name.find_last_of("/\\"); + if (std::string::npos != dir_name_pos) + { + image_asset_file_name = file_name.substr(0U, dir_name_pos + 1U); + } + else + { + image_asset_file_name += "./"; + } + + size_t ext_name_pos = meterial_texture_image_uri.find_last_of("."); + if (std::string::npos != ext_name_pos) + { + image_asset_file_name += meterial_texture_image_uri.substr(0U, ext_name_pos + 1U); + } + else + { + image_asset_file_name += meterial_texture_image_uri; + } + + image_asset_file_name_dds = (image_asset_file_name + "dds"); + image_asset_file_name_pvr = (image_asset_file_name + "pvr"); + } + + import_asset_input_stream *import_image_asset_input_stream; + bool (*pfn_import_image_asset_header_from_input_stream)(import_asset_input_stream *, BRX_SAMPLED_ASSET_IMAGE_IMPORT_HEADER *, size_t *); + bool (*pfn_import_image_asset_data_from_input_stream)(import_asset_input_stream *, BRX_SAMPLED_ASSET_IMAGE_IMPORT_HEADER const *, size_t, void *, size_t, BRX_SAMPLED_ASSET_IMAGE_IMPORT_SUBRESOURCE_MEMCPY_DEST const *); + if (device->is_sampled_asset_image_compression_bc_supported() && (NULL != (import_image_asset_input_stream = input_stream_factory->create_instance(image_asset_file_name_dds.c_str())))) + { + pfn_import_image_asset_header_from_input_stream = import_dds_image_asset_header_from_input_stream; + pfn_import_image_asset_data_from_input_stream = import_dds_image_asset_data_from_input_stream; + } + else if (device->is_sampled_asset_image_compression_astc_supported() && (NULL != (import_image_asset_input_stream = input_stream_factory->create_instance(image_asset_file_name_pvr.c_str())))) + { + pfn_import_image_asset_header_from_input_stream = import_pvr_image_asset_header_from_input_stream; + pfn_import_image_asset_data_from_input_stream = import_pvr_image_asset_data_from_input_stream; + } + else + { + // TODO: jpeg + // TODO: png + import_image_asset_input_stream = NULL; + pfn_import_image_asset_header_from_input_stream = NULL; + pfn_import_image_asset_data_from_input_stream = NULL; + } + + if (NULL != import_image_asset_input_stream && NULL != pfn_import_image_asset_header_from_input_stream && NULL != pfn_import_image_asset_data_from_input_stream) + { + BRX_SAMPLED_ASSET_IMAGE_IMPORT_HEADER image_asset_header; + std::vector subresource_memcpy_dests; + + size_t image_asset_data_offset; + bool const res_import_image_asset_header = pfn_import_image_asset_header_from_input_stream(import_image_asset_input_stream, &image_asset_header, &image_asset_data_offset); + assert(res_import_image_asset_header); + + uint32_t const subresource_count = image_asset_header.mip_levels; + subresource_memcpy_dests.resize(subresource_count); + + // TODO: support more image paramters + assert(!image_asset_header.is_cube_map); + assert(BRX_SAMPLED_ASSET_IMAGE_TYPE_2D == image_asset_header.type); + assert(1U == image_asset_header.depth); + assert(1U == image_asset_header.array_layers); + uint32_t const total_bytes = brx_sampled_asset_image_import_calculate_subresource_memcpy_dests(image_asset_header.format, image_asset_header.width, image_asset_header.height, 1U, image_asset_header.mip_levels, 1U, 0U, staging_upload_buffer_offset_alignment, staging_upload_buffer_row_pitch_alignment, subresource_count, &subresource_memcpy_dests[0]); + brx_staging_upload_buffer *const image_staging_upload_buffer = device->create_staging_upload_buffer(static_cast(total_bytes)); + staging_upload_buffers.push_back(image_staging_upload_buffer); + + bool const res_import_image_asset_data = pfn_import_image_asset_data_from_input_stream(import_image_asset_input_stream, &image_asset_header, image_asset_data_offset, image_staging_upload_buffer->get_host_memory_range_base(), subresource_count, &subresource_memcpy_dests[0]); + assert(res_import_image_asset_data); + + input_stream_factory->destory_instance(import_image_asset_input_stream); + + out_subset.m_material_textures[mesh_subset_meterial_texture_index] = device->create_sampled_asset_image(image_asset_header.format, image_asset_header.width, image_asset_header.height, image_asset_header.mip_levels); + + for (uint32_t mip_level = 0U; mip_level < image_asset_header.mip_levels; ++mip_level) + { + upload_command_buffer->upload_from_staging_upload_buffer_to_sampled_asset_image(out_subset.m_material_textures[mesh_subset_meterial_texture_index], image_asset_header.format, image_asset_header.width, image_asset_header.height, mip_level, image_staging_upload_buffer, subresource_memcpy_dests[mip_level].staging_upload_buffer_offset, subresource_memcpy_dests[mip_level].output_row_pitch, subresource_memcpy_dests[mip_level].output_row_count); + } + } + else + { + out_subset.m_material_textures[mesh_subset_meterial_texture_index] = NULL; + } + } + else + { + out_subset.m_material_textures[mesh_subset_meterial_texture_index] = NULL; + } + } + } + } + + out_mesh.m_instances.resize(total_mesh_data[mesh_index].m_instances.size()); + + for (size_t instance_index = 0U; instance_index < total_mesh_data[mesh_index].m_instances.size(); ++instance_index) + { + scene_mesh_instance_data &in_instance_data = total_mesh_data[mesh_index].m_instances[instance_index]; + + Demo_Mesh_Instance &out_mesh_instance = out_mesh.m_instances[instance_index]; + + out_mesh_instance.m_model_transform = std::move(in_instance_data.m_model_transform); + out_mesh_instance.m_animation_skeleton = std::move(in_instance_data.m_animation_skeleton); + + if (!in_mesh_data.m_skinned) + { + assert(out_mesh_instance.m_subsets.empty()); + } + else + { + out_mesh_instance.m_subsets.resize(in_mesh_data.m_subsets.size()); + + for (size_t subset_index = 0U; subset_index < in_mesh_data.m_subsets.size(); ++subset_index) + { + scene_mesh_subset_data const &in_subset_data = in_mesh_data.m_subsets[subset_index]; + + Demo_Mesh_Skinned_Subset &out_mesh_skinned_subset = out_mesh_instance.m_subsets[subset_index]; + + uint32_t const vertex_count = static_cast(in_subset_data.m_vertex_position_binding.size()); + + uint32_t const vertex_position_buffer_size = sizeof(scene_mesh_vertex_position_binding) * vertex_count; + + out_mesh_skinned_subset.m_skinned_vertex_position_buffer = device->create_storage_intermediate_buffer(vertex_position_buffer_size); + + assert(vertex_count == in_subset_data.m_vertex_varying_binding.size()); + + uint32_t const vertex_varying_buffer_size = sizeof(scene_mesh_vertex_varying_binding) * vertex_count; + + out_mesh_skinned_subset.m_skinned_vertex_varying_buffer = device->create_storage_intermediate_buffer(vertex_varying_buffer_size); + } + } + } + } + } + } + + import_asset_destroy_file_input_stream_factory(input_stream_factory); + input_stream_factory = NULL; + + // Place Holder Texture + { + uint32_t const staging_upload_buffer_offset_alignment = device->get_staging_upload_buffer_offset_alignment(); + uint32_t const staging_upload_buffer_row_pitch_alignment = device->get_staging_upload_buffer_row_pitch_alignment(); + + BRX_SAMPLED_ASSET_IMAGE_FORMAT const format = BRX_SAMPLED_ASSET_IMAGE_FORMAT_R8G8B8A8_UNORM; + uint32_t const width = 1U; + uint32_t const height = 1U; + uint32_t const mip_levels = 1U; + this->m_place_holder_texture = device->create_sampled_asset_image(format, width, height, mip_levels); + + std::vector subresource_memcpy_dests; + uint32_t const subresource_count = mip_levels; + subresource_memcpy_dests.resize(subresource_count); + + uint32_t total_bytes = brx_sampled_asset_image_import_calculate_subresource_memcpy_dests(format, width, height, 1U, mip_levels, 1U, 0U, staging_upload_buffer_offset_alignment, staging_upload_buffer_row_pitch_alignment, subresource_count, &subresource_memcpy_dests[0]); + brx_staging_upload_buffer *place_holder_image_staging_upload_buffer = device->create_staging_upload_buffer(total_bytes); + + staging_upload_buffers.push_back(place_holder_image_staging_upload_buffer); + + uint32_t const mip_level = 0U; + uint32_t const subresource_index = brx_sampled_asset_image_import_calculate_subresource_index(mip_level, 0U, 0U, mip_levels, 1U); + for (uint32_t output_slice_index = 0U; output_slice_index < subresource_memcpy_dests[mip_level].output_slice_count; ++output_slice_index) + { + for (uint32_t output_row_index = 0U; output_row_index < subresource_memcpy_dests[mip_level].output_row_count; ++output_row_index) + { + void *destination = reinterpret_cast(reinterpret_cast(place_holder_image_staging_upload_buffer->get_host_memory_range_base()) + (subresource_memcpy_dests[subresource_index].staging_upload_buffer_offset + subresource_memcpy_dests[subresource_index].output_slice_pitch * output_slice_index + subresource_memcpy_dests[subresource_index].output_row_pitch * output_row_index)); + + std::memset(destination, 0, subresource_memcpy_dests[mip_level].output_row_size); + } + } + + upload_command_buffer->upload_from_staging_upload_buffer_to_sampled_asset_image(this->m_place_holder_texture, format, width, height, mip_level, place_holder_image_staging_upload_buffer, subresource_memcpy_dests[mip_level].staging_upload_buffer_offset, subresource_memcpy_dests[mip_level].output_row_pitch, subresource_memcpy_dests[mip_level].output_row_count); + } + + // store + // release + // acquire + { + std::vector uploaded_storage_asset_buffers; + std::vector uploaded_sampled_asset_images; + std::vector uploaded_destination_mip_levels; + + // Asset + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh const &scene_mesh = this->m_scene_meshes[mesh_index]; + + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Subset const &scene_mesh_subset = scene_mesh.m_subsets[subset_index]; + + uploaded_storage_asset_buffers.push_back(scene_mesh_subset.m_vertex_position_buffer); + + uploaded_storage_asset_buffers.push_back(scene_mesh_subset.m_vertex_varying_buffer); + + if (scene_mesh.m_skinned) + { + uploaded_storage_asset_buffers.push_back(scene_mesh_subset.m_vertex_joint_buffer); + } + + uploaded_storage_asset_buffers.push_back(scene_mesh_subset.m_index_buffer); + + uploaded_storage_asset_buffers.push_back(scene_mesh_subset.m_material_buffer); + + for (uint32_t mesh_subset_meterial_texture_index = 0U; mesh_subset_meterial_texture_index < DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT; ++mesh_subset_meterial_texture_index) + { + brx_sampled_asset_image *const material_texture = scene_mesh_subset.m_material_textures[mesh_subset_meterial_texture_index]; + + if (NULL != material_texture) + { + uint32_t const mip_level_count = material_texture->get_mip_levels(); + + for (uint32_t mip_level = 0U; mip_level < mip_level_count; ++mip_level) + { + uploaded_sampled_asset_images.push_back(material_texture); + + uploaded_destination_mip_levels.push_back(mip_level); + } + } + } + } + } + + // Place Holder Texture + { + uploaded_sampled_asset_images.push_back(this->m_place_holder_texture); + + uploaded_destination_mip_levels.push_back(0U); + } + + assert(uploaded_sampled_asset_images.size() == uploaded_destination_mip_levels.size()); + + upload_command_buffer->release(static_cast(uploaded_storage_asset_buffers.size()), uploaded_storage_asset_buffers.data(), static_cast(uploaded_sampled_asset_images.size()), &uploaded_sampled_asset_images[0], uploaded_destination_mip_levels.data(), 0U, NULL); + + graphics_command_buffer->acquire(static_cast(uploaded_storage_asset_buffers.size()), uploaded_storage_asset_buffers.data(), static_cast(uploaded_sampled_asset_images.size()), &uploaded_sampled_asset_images[0], uploaded_destination_mip_levels.data(), 0U, NULL); + } + + upload_command_buffer->end(); + + graphics_command_buffer->end(); + + upload_queue->submit_and_signal(upload_command_buffer); + + device->reset_fence(fence); + + graphics_queue->wait_and_submit(upload_command_buffer, graphics_command_buffer, fence); + + device->wait_for_fence(fence); + + for (brx_staging_upload_buffer *const staging_upload_buffer : staging_upload_buffers) + { + device->destroy_staging_upload_buffer(staging_upload_buffer); + } + + staging_upload_buffers.clear(); + } + + device->destroy_fence(fence); + + device->destroy_upload_command_buffer(upload_command_buffer); + + device->destroy_graphics_command_buffer(graphics_command_buffer); + + device->destroy_upload_queue(upload_queue); + + device->destroy_graphics_queue(graphics_queue); + } + + // Sampler + { + this->m_sampler = device->create_sampler(BRX_SAMPLER_FILTER_LINEAR); + } + + // Uniform Buffer + { + this->m_uniform_upload_buffer_offset_alignment = device->get_uniform_upload_buffer_offset_alignment(); + + this->m_common_none_update_uniform_buffer = device->create_uniform_upload_buffer(tbb_align_up(static_cast(sizeof(common_none_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * FRAME_THROTTLING_COUNT); + + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh &scene_mesh = this->m_scene_meshes[mesh_index]; + + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + if (!scene_mesh.m_skinned) + { + scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_uniform_buffer = NULL; + } + else + { + scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_uniform_buffer = device->create_uniform_upload_buffer(tbb_align_up(static_cast(sizeof(skin_pipeline_per_mesh_instance_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * FRAME_THROTTLING_COUNT); + } + + scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_uniform_buffer = device->create_uniform_upload_buffer(tbb_align_up(static_cast(sizeof(gbuffer_pipeline_per_mesh_instance_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * FRAME_THROTTLING_COUNT); + } + } + } + + // Descriptor + { + // GBuffer Pipeline + { + { + this->m_gbuffer_pipeline_none_update_descriptor_set = device->create_descriptor_set(gbuffer_pipeline_none_update_descriptor_set_layout); + + constexpr uint32_t const dynamic_uniform_buffers_range = sizeof(common_none_update_set_uniform_buffer_binding); + device->write_descriptor_set(this->m_gbuffer_pipeline_none_update_descriptor_set, 0U, BRX_DESCRIPTOR_TYPE_DYNAMIC_UNIFORM_BUFFER, 0U, 1U, &this->m_common_none_update_uniform_buffer, &dynamic_uniform_buffers_range, NULL, NULL, NULL, NULL, NULL, NULL); + + device->write_descriptor_set(this->m_gbuffer_pipeline_none_update_descriptor_set, 1U, BRX_DESCRIPTOR_TYPE_SAMPLER, 0U, 1U, NULL, NULL, NULL, NULL, NULL, NULL, &this->m_sampler, NULL); + } + + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh &scene_mesh = this->m_scene_meshes[mesh_index]; + + if (!scene_mesh.m_skinned) + { + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Subset &scene_mesh_subset = scene_mesh.m_subsets[subset_index]; + + scene_mesh_subset.m_gbuffer_pipeline_per_mesh_subset_update_descriptor_set = device->create_descriptor_set(gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout); + + brx_read_only_storage_buffer const *const read_only_storage_buffers[] = { + scene_mesh_subset.m_vertex_position_buffer->get_read_only_storage_buffer(), + scene_mesh_subset.m_vertex_varying_buffer->get_read_only_storage_buffer(), + scene_mesh_subset.m_index_buffer->get_read_only_storage_buffer(), + scene_mesh_subset.m_material_buffer->get_read_only_storage_buffer()}; + device->write_descriptor_set(scene_mesh_subset.m_gbuffer_pipeline_per_mesh_subset_update_descriptor_set, 0U, BRX_DESCRIPTOR_TYPE_READ_ONLY_STORAGE_BUFFER, 0U, sizeof(read_only_storage_buffers) / sizeof(read_only_storage_buffers[0]), NULL, NULL, read_only_storage_buffers, NULL, NULL, NULL, NULL, NULL); + + brx_sampled_image const *sample_images[DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT]; + for (uint32_t mesh_subset_meterial_texture_index = 0U; mesh_subset_meterial_texture_index < DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT; ++mesh_subset_meterial_texture_index) + { + brx_sampled_asset_image *material_texture = scene_mesh_subset.m_material_textures[mesh_subset_meterial_texture_index]; + + sample_images[mesh_subset_meterial_texture_index] = (NULL != material_texture) ? material_texture->get_sampled_image() : this->m_place_holder_texture->get_sampled_image(); + } + device->write_descriptor_set(scene_mesh_subset.m_gbuffer_pipeline_per_mesh_subset_update_descriptor_set, 4U, BRX_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0U, sizeof(sample_images) / sizeof(sample_images[0]), NULL, NULL, NULL, NULL, sample_images, NULL, NULL, NULL); + } + + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + assert(scene_mesh_instance.m_subsets.empty()); + + scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_descriptor_set = NULL; + + scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_descriptor_set = device->create_descriptor_set(gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout); + + constexpr uint32_t const dynamic_uniform_buffers_range = sizeof(gbuffer_pipeline_per_mesh_instance_update_set_uniform_buffer_binding); + device->write_descriptor_set(scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_descriptor_set, 0U, BRX_DESCRIPTOR_TYPE_DYNAMIC_UNIFORM_BUFFER, 0U, 1U, &scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_uniform_buffer, &dynamic_uniform_buffers_range, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + else + { + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Subset &scene_mesh_subset = scene_mesh.m_subsets[subset_index]; + + scene_mesh_subset.m_gbuffer_pipeline_per_mesh_subset_update_descriptor_set = NULL; + } + + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + assert(scene_mesh.m_subsets.size() == scene_mesh_instance.m_subsets.size()); + + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Subset const &scene_mesh_subset = scene_mesh.m_subsets[subset_index]; + + Demo_Mesh_Skinned_Subset &scene_mesh_skinned_subset = scene_mesh_instance.m_subsets[subset_index]; + + { + scene_mesh_skinned_subset.m_skin_pipeline_per_mesh_skinned_subset_update_descriptor_set = device->create_descriptor_set(skin_pipeline_per_mesh_skinned_subset_update_descriptor_set_layout); + + brx_read_only_storage_buffer const *const read_only_storage_buffers[] = { + scene_mesh_subset.m_vertex_position_buffer->get_read_only_storage_buffer(), + scene_mesh_subset.m_vertex_varying_buffer->get_read_only_storage_buffer(), + scene_mesh_subset.m_vertex_joint_buffer->get_read_only_storage_buffer()}; + device->write_descriptor_set(scene_mesh_skinned_subset.m_skin_pipeline_per_mesh_skinned_subset_update_descriptor_set, 0U, BRX_DESCRIPTOR_TYPE_READ_ONLY_STORAGE_BUFFER, 0U, sizeof(read_only_storage_buffers) / sizeof(read_only_storage_buffers[0]), NULL, NULL, read_only_storage_buffers, NULL, NULL, NULL, NULL, NULL); + + brx_storage_buffer const *const storage_buffers[] = { + scene_mesh_skinned_subset.m_skinned_vertex_position_buffer->get_storage_buffer(), + scene_mesh_skinned_subset.m_skinned_vertex_varying_buffer->get_storage_buffer()}; + device->write_descriptor_set(scene_mesh_skinned_subset.m_skin_pipeline_per_mesh_skinned_subset_update_descriptor_set, 1U, BRX_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0U, sizeof(storage_buffers) / sizeof(storage_buffers[0]), NULL, NULL, NULL, storage_buffers, NULL, NULL, NULL, NULL); + } + + { + scene_mesh_skinned_subset.m_gbuffer_pipeline_per_mesh_skinned_subset_update_descriptor_set = device->create_descriptor_set(gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout); + + brx_read_only_storage_buffer const *const read_only_storage_buffers[] = { + scene_mesh_skinned_subset.m_skinned_vertex_position_buffer->get_read_only_storage_buffer(), + scene_mesh_skinned_subset.m_skinned_vertex_varying_buffer->get_read_only_storage_buffer(), + scene_mesh_subset.m_index_buffer->get_read_only_storage_buffer(), + scene_mesh_subset.m_material_buffer->get_read_only_storage_buffer()}; + device->write_descriptor_set(scene_mesh_skinned_subset.m_gbuffer_pipeline_per_mesh_skinned_subset_update_descriptor_set, 0U, BRX_DESCRIPTOR_TYPE_READ_ONLY_STORAGE_BUFFER, 0U, sizeof(read_only_storage_buffers) / sizeof(read_only_storage_buffers[0]), NULL, NULL, read_only_storage_buffers, NULL, NULL, NULL, NULL, NULL); + + brx_sampled_image const *sample_images[DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT]; + for (uint32_t mesh_subset_meterial_texture_index = 0U; mesh_subset_meterial_texture_index < DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT; ++mesh_subset_meterial_texture_index) + { + brx_sampled_asset_image *material_texture = scene_mesh_subset.m_material_textures[mesh_subset_meterial_texture_index]; + + sample_images[mesh_subset_meterial_texture_index] = (NULL != material_texture) ? material_texture->get_sampled_image() : this->m_place_holder_texture->get_sampled_image(); + } + device->write_descriptor_set(scene_mesh_skinned_subset.m_gbuffer_pipeline_per_mesh_skinned_subset_update_descriptor_set, 4U, BRX_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0U, sizeof(sample_images) / sizeof(sample_images[0]), NULL, NULL, NULL, NULL, sample_images, NULL, NULL, NULL); + } + } + + { + scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_descriptor_set = device->create_descriptor_set(skin_pipeline_per_mesh_instance_update_descriptor_set_layout); + + constexpr uint32_t const dynamic_uniform_buffers_range = sizeof(skin_pipeline_per_mesh_instance_update_set_uniform_buffer_binding); + device->write_descriptor_set(scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_descriptor_set, 0U, BRX_DESCRIPTOR_TYPE_DYNAMIC_UNIFORM_BUFFER, 0U, 1U, &scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_uniform_buffer, &dynamic_uniform_buffers_range, NULL, NULL, NULL, NULL, NULL, NULL); + } + + { + scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_descriptor_set = device->create_descriptor_set(gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout); + + constexpr uint32_t const dynamic_uniform_buffers_range = sizeof(gbuffer_pipeline_per_mesh_instance_update_set_uniform_buffer_binding); + device->write_descriptor_set(scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_descriptor_set, 0U, BRX_DESCRIPTOR_TYPE_DYNAMIC_UNIFORM_BUFFER, 0U, 1U, &scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_uniform_buffer, &dynamic_uniform_buffers_range, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + } + } + } + + // Deferred Shading Pipeline + { + { + this->m_deferred_shading_pipeline_none_update_descriptor_set = device->create_descriptor_set(this->m_deferred_shading_pipeline_none_update_descriptor_set_layout); + + constexpr uint32_t const dynamic_uniform_buffers_range = sizeof(common_none_update_set_uniform_buffer_binding); + device->write_descriptor_set(this->m_deferred_shading_pipeline_none_update_descriptor_set, 0U, BRX_DESCRIPTOR_TYPE_DYNAMIC_UNIFORM_BUFFER, 0U, 1U, &this->m_common_none_update_uniform_buffer, &dynamic_uniform_buffers_range, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + } + + device->destroy_descriptor_set_layout(skin_pipeline_per_mesh_instance_update_descriptor_set_layout); + skin_pipeline_per_mesh_instance_update_descriptor_set_layout = NULL; + device->destroy_descriptor_set_layout(skin_pipeline_per_mesh_skinned_subset_update_descriptor_set_layout); + skin_pipeline_per_mesh_skinned_subset_update_descriptor_set_layout = NULL; + device->destroy_descriptor_set_layout(gbuffer_pipeline_none_update_descriptor_set_layout); + gbuffer_pipeline_none_update_descriptor_set_layout = NULL; + device->destroy_descriptor_set_layout(gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout); + gbuffer_pipeline_per_mesh_subset_update_descriptor_set_layout = NULL; + device->destroy_descriptor_set_layout(gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout); + gbuffer_pipeline_per_mesh_instance_update_descriptor_set_layout = NULL; + + // Init Intermediate Images + this->m_intermediate_width = 0U; + this->m_intermediate_height = 0U; + this->m_gbuffer_color_attachment = NULL; + this->m_gbuffer_depth_attachment = NULL; + this->m_deferred_shading_color_attachment = NULL; + this->m_gbuffer_frame_buffer = NULL; + this->m_deferred_shading_frame_buffer = NULL; + + // Init Camera + g_camera_controller.m_eye_position = DirectX::XMFLOAT3(0.0F, 1.3F, 1.0F); + g_camera_controller.m_eye_direction = DirectX::XMFLOAT3(0.0F, -0.36F, -0.98F); + g_camera_controller.m_up_direction = DirectX::XMFLOAT3(0.0F, 1.0F, 0.0F); + g_camera_fov = DirectX::XM_PI * (1.0F / 3.0F); + + // Init Animation Time + this->m_animation_time = 0.0F; +} + +void Demo::destroy(brx_device *device) +{ + // Descriptor + { + // GBuffer Pipeline + { + device->destroy_descriptor_set(this->m_gbuffer_pipeline_none_update_descriptor_set); + + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh &scene_mesh = this->m_scene_meshes[mesh_index]; + + if (!scene_mesh.m_skinned) + { + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Subset &scene_mesh_subset = scene_mesh.m_subsets[subset_index]; + + device->destroy_descriptor_set(scene_mesh_subset.m_gbuffer_pipeline_per_mesh_subset_update_descriptor_set); + } + + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + assert(scene_mesh_instance.m_subsets.empty()); + + assert(NULL == scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_descriptor_set); + + device->destroy_descriptor_set(scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_descriptor_set); + } + } + else + { + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Subset &scene_mesh_subset = scene_mesh.m_subsets[subset_index]; + + assert(NULL == scene_mesh_subset.m_gbuffer_pipeline_per_mesh_subset_update_descriptor_set); + } + + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Skinned_Subset &scene_mesh_skinned_subset = scene_mesh_instance.m_subsets[subset_index]; + + device->destroy_descriptor_set(scene_mesh_skinned_subset.m_skin_pipeline_per_mesh_skinned_subset_update_descriptor_set); + + device->destroy_descriptor_set(scene_mesh_skinned_subset.m_gbuffer_pipeline_per_mesh_skinned_subset_update_descriptor_set); + } + + device->destroy_descriptor_set(scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_descriptor_set); + + device->destroy_descriptor_set(scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_descriptor_set); + } + } + } + } + + // Deferred Shading Pipeline + { + device->destroy_descriptor_set_layout(this->m_deferred_shading_pipeline_none_update_descriptor_set_layout); + + device->destroy_descriptor_set(this->m_deferred_shading_pipeline_none_update_descriptor_set); + } + } + + // Uniform Buffer + { + device->destroy_uniform_upload_buffer(this->m_common_none_update_uniform_buffer); + + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh &scene_mesh = this->m_scene_meshes[mesh_index]; + + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + if (!scene_mesh.m_skinned) + { + assert(NULL == scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_uniform_buffer); + } + else + { + device->destroy_uniform_upload_buffer(scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_uniform_buffer); + } + + device->destroy_uniform_upload_buffer(scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_uniform_buffer); + } + } + } + + // Sampler + { + device->destroy_sampler(this->m_sampler); + } + + // Place Holder Texture + { + device->destroy_sampled_asset_image(this->m_place_holder_texture); + } + + // Asset + { + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh &scene_mesh = this->m_scene_meshes[mesh_index]; + + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Subset &scene_mesh_subset = scene_mesh.m_subsets[subset_index]; + + device->destroy_storage_asset_buffer(scene_mesh_subset.m_vertex_position_buffer); + + device->destroy_storage_asset_buffer(scene_mesh_subset.m_vertex_varying_buffer); + + if (scene_mesh.m_skinned) + { + device->destroy_storage_asset_buffer(scene_mesh_subset.m_vertex_joint_buffer); + } + else + { + assert(NULL == scene_mesh_subset.m_vertex_joint_buffer); + } + + device->destroy_storage_asset_buffer(scene_mesh_subset.m_index_buffer); + + device->destroy_storage_asset_buffer(scene_mesh_subset.m_material_buffer); + + for (uint32_t mesh_subset_meterial_texture_index = 0U; mesh_subset_meterial_texture_index < DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT; ++mesh_subset_meterial_texture_index) + { + brx_sampled_asset_image *const material_texture = scene_mesh_subset.m_material_textures[mesh_subset_meterial_texture_index]; + + if (NULL != material_texture) + { + device->destroy_sampled_asset_image(material_texture); + } + } + } + + if (!scene_mesh.m_skinned) + { + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + assert(scene_mesh_instance.m_subsets.empty()); + } + } + else + { + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Skinned_Subset &scene_mesh_skinned_subset = scene_mesh_instance.m_subsets[subset_index]; + + device->destroy_storage_intermediate_buffer(scene_mesh_skinned_subset.m_skinned_vertex_position_buffer); + + device->destroy_storage_intermediate_buffer(scene_mesh_skinned_subset.m_skinned_vertex_varying_buffer); + } + } + } + } + } + + // Render Pass and Pipeline + { + device->destroy_graphics_pipeline(this->m_gbuffer_pipeline); + + device->destroy_pipeline_layout(this->m_gbuffer_pipeline_layout); + + device->destroy_render_pass(this->m_gbuffer_render_pass); + + device->destroy_graphics_pipeline(this->m_deferred_shading_pipeline); + + device->destroy_pipeline_layout(this->m_deferred_shading_pipeline_layout); + + device->destroy_render_pass(this->m_deferred_shading_render_pass); + + device->destroy_compute_pipeline(this->m_skin_pipeline); + + device->destroy_pipeline_layout(this->m_skin_pipeline_layout); + } +} + +void Demo::on_swap_chain_attach(brx_device *device, uint32_t swap_chain_image_width, uint32_t swap_chain_image_height) +{ + assert(0U == this->m_intermediate_width); + assert(0U == this->m_intermediate_height); + + this->m_intermediate_width = swap_chain_image_width; + this->m_intermediate_height = swap_chain_image_height; + + // Intermediate Images + { + assert(NULL == this->m_gbuffer_color_attachment); + assert(NULL == this->m_gbuffer_depth_attachment); + assert(NULL == this->m_deferred_shading_color_attachment); + + this->m_gbuffer_color_attachment = device->create_color_attachment_image(this->m_gbuffer_color_attachement_format, this->m_intermediate_width, this->m_intermediate_height, true); + this->m_gbuffer_depth_attachment = device->create_depth_stencil_attachment_image(this->m_gbuffer_depth_attachement_format, this->m_intermediate_width, this->m_intermediate_height, true); + this->m_deferred_shading_color_attachment = device->create_color_attachment_image(this->m_deferred_shading_color_attachement_format, this->m_intermediate_width, this->m_intermediate_height, true); + } + + // Frame Buffer + { + assert(NULL == this->m_gbuffer_frame_buffer); + assert(NULL == this->m_deferred_shading_frame_buffer); + + this->m_gbuffer_frame_buffer = device->create_frame_buffer(this->m_gbuffer_render_pass, this->m_intermediate_width, this->m_intermediate_height, 1U, &this->m_gbuffer_color_attachment, this->m_gbuffer_depth_attachment); + this->m_deferred_shading_frame_buffer = device->create_frame_buffer(this->m_deferred_shading_render_pass, this->m_intermediate_width, this->m_intermediate_height, 1U, &this->m_deferred_shading_color_attachment, NULL); + } + + // Descriptor + { + // Deferred Shading Pipeline + { + // The VkDescriptorSetLayout should still be valid when perform write update on VkDescriptorSet + assert(NULL != this->m_deferred_shading_pipeline_none_update_descriptor_set_layout); + { + { + brx_sampled_image const *const sampled_images[] = { + this->m_gbuffer_color_attachment->get_sampled_image()}; + device->write_descriptor_set(this->m_deferred_shading_pipeline_none_update_descriptor_set, 1U, BRX_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0U, sizeof(sampled_images) / sizeof(sampled_images[0]), NULL, NULL, NULL, NULL, sampled_images, NULL, NULL, NULL); + } + + { + brx_sampled_image const *const sampled_images[] = { + this->m_gbuffer_depth_attachment->get_sampled_image()}; + device->write_descriptor_set(this->m_deferred_shading_pipeline_none_update_descriptor_set, 2U, BRX_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0U, sizeof(sampled_images) / sizeof(sampled_images[0]), NULL, NULL, NULL, NULL, sampled_images, NULL, NULL, NULL); + } + } + } + } +} + +brx_sampled_image const *Demo::get_sampled_image_for_present() +{ + return this->m_deferred_shading_color_attachment->get_sampled_image(); +} + +void Demo::on_swap_chain_dettach(brx_device *device) +{ + device->destroy_frame_buffer(this->m_gbuffer_frame_buffer); + device->destroy_frame_buffer(this->m_deferred_shading_frame_buffer); + + this->m_gbuffer_frame_buffer = NULL; + this->m_deferred_shading_frame_buffer = NULL; + + device->destroy_color_attachment_image(this->m_deferred_shading_color_attachment); + device->destroy_depth_stencil_attachment_image(this->m_gbuffer_depth_attachment); + device->destroy_color_attachment_image(this->m_gbuffer_color_attachment); + + this->m_deferred_shading_color_attachment = NULL; + this->m_gbuffer_depth_attachment = NULL; + this->m_gbuffer_color_attachment = NULL; + + this->m_intermediate_width = 0U; + this->m_intermediate_height = 0U; +} + +void Demo::draw(brx_graphics_command_buffer *command_buffer, float interval_time, uint32_t frame_throttling_index) +{ + // Update Uniform Buffer + { + // Skin Pipeline - Per Mesh Instance Update + { + this->m_animation_time += interval_time; + + // 60 FPS + size_t animetion_frame_index = static_cast(60.0 * this->m_animation_time); + + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh const &scene_mesh = this->m_scene_meshes[mesh_index]; + + if (scene_mesh.m_skinned) + { + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance const &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + skin_pipeline_per_mesh_instance_update_set_uniform_buffer_binding *const skin_pipeline_per_mesh_instance_update_set_uniform_buffer_binding_destination = reinterpret_cast(reinterpret_cast(scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_uniform_buffer->get_host_memory_range_base()) + tbb_align_up(static_cast(sizeof(skin_pipeline_per_mesh_instance_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * frame_throttling_index); + + scene_animation_pose const &pose = scene_mesh_instance.m_animation_skeleton.get_pose(animetion_frame_index); + + assert(pose.get_joint_count() <= MAX_JOINT_COUNT); + + for (uint32_t joint_index = 0U; joint_index < pose.get_joint_count(); ++joint_index) + { + unit_dual_quaternion_from_rigid_transform(&skin_pipeline_per_mesh_instance_update_set_uniform_buffer_binding_destination->g_dual_quaternions[2 * joint_index], pose.get_quaternion(joint_index), pose.get_translation(joint_index)); + } + } + } + } + } + + // Common - None Update (update frequency denotes the updating of the resource bindings instead of the data) + { + common_none_update_set_uniform_buffer_binding *const common_none_update_set_uniform_buffer_binding_destination = reinterpret_cast(reinterpret_cast(this->m_common_none_update_uniform_buffer->get_host_memory_range_base()) + tbb_align_up(static_cast(sizeof(common_none_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * frame_throttling_index); + + DirectX::XMFLOAT3 eye_position = g_camera_controller.m_eye_position; + DirectX::XMFLOAT3 eye_direction = g_camera_controller.m_eye_direction; + DirectX::XMFLOAT3 up_direction = g_camera_controller.m_up_direction; + DirectX::XMMATRIX view_transform = DirectX::XMMatrixLookToRH(DirectX::XMLoadFloat3(&eye_position), DirectX::XMLoadFloat3(&eye_direction), DirectX::XMLoadFloat3(&up_direction)); + DirectX::XMMATRIX projection_transform = DirectX_Math_Matrix_PerspectiveFovRH_ReversedZ(g_camera_fov, static_cast(this->m_intermediate_width) / static_cast(this->m_intermediate_height), 0.1F, 1000.0F); + + DirectX::XMStoreFloat4x4(&common_none_update_set_uniform_buffer_binding_destination->g_view_transform, view_transform); + DirectX::XMStoreFloat4x4(&common_none_update_set_uniform_buffer_binding_destination->g_projection_transform, projection_transform); + DirectX::XMStoreFloat4x4(&common_none_update_set_uniform_buffer_binding_destination->g_inverse_view_transform, DirectX::XMMatrixInverse(NULL, view_transform)); + DirectX::XMStoreFloat4x4(&common_none_update_set_uniform_buffer_binding_destination->g_inverse_projection_transform, DirectX::XMMatrixInverse(NULL, projection_transform)); + + common_none_update_set_uniform_buffer_binding_destination->g_screen_width = static_cast(this->m_intermediate_width); + common_none_update_set_uniform_buffer_binding_destination->g_screen_height = static_cast(this->m_intermediate_height); + } + + // GBuffer Pipeline - Per Mesh Instance Update + { + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh const &scene_mesh = this->m_scene_meshes[mesh_index]; + + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance const &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + gbuffer_pipeline_per_mesh_instance_update_set_uniform_buffer_binding *const gbuffer_pipeline_per_mesh_instance_update_set_uniform_buffer_binding_destination = reinterpret_cast(reinterpret_cast(scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_uniform_buffer->get_host_memory_range_base()) + tbb_align_up(static_cast(sizeof(gbuffer_pipeline_per_mesh_instance_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * frame_throttling_index); + + gbuffer_pipeline_per_mesh_instance_update_set_uniform_buffer_binding_destination->g_model_transform = scene_mesh_instance.m_model_transform; + } + } + } + } + + // Skin Pass + { + std::vector skinned_buffers; + // TODO: can we remove this traverse? + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh const &scene_mesh = this->m_scene_meshes[mesh_index]; + + if (scene_mesh.m_skinned) + { + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance const &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Skinned_Subset const &scene_mesh_skinned_subset = scene_mesh_instance.m_subsets[subset_index]; + + skinned_buffers.push_back(scene_mesh_skinned_subset.m_skinned_vertex_position_buffer->get_storage_buffer()); + skinned_buffers.push_back(scene_mesh_skinned_subset.m_skinned_vertex_varying_buffer->get_storage_buffer()); + } + } + } + } + + if (!skinned_buffers.empty()) + { + command_buffer->begin_debug_utils_label("Skin Pass"); + + std::vector skinned_buffer_load_operations(skinned_buffers.size(), BRX_COMPUTE_PASS_STORAGE_BUFFER_LOAD_OPERATION_DONT_CARE); + command_buffer->compute_pass_load(static_cast(skinned_buffers.size()), skinned_buffers.data(), skinned_buffer_load_operations.data(), 0U, NULL, NULL); + + command_buffer->bind_compute_pipeline(this->m_skin_pipeline); + + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh const &scene_mesh = this->m_scene_meshes[mesh_index]; + + if (scene_mesh.m_skinned) + { + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance const &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Subset const &scene_mesh_subset = scene_mesh.m_subsets[subset_index]; + + Demo_Mesh_Skinned_Subset const &scene_mesh_skinned_subset = scene_mesh_instance.m_subsets[subset_index]; + + brx_descriptor_set *const descritor_sets[] = { + scene_mesh_instance.m_skin_pipeline_per_mesh_instance_update_descriptor_set, + scene_mesh_skinned_subset.m_skin_pipeline_per_mesh_skinned_subset_update_descriptor_set}; + + uint32_t const dynamic_offsets[] = { + tbb_align_up(static_cast(sizeof(skin_pipeline_per_mesh_instance_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * frame_throttling_index}; + + command_buffer->bind_compute_descriptor_sets(this->m_skin_pipeline_layout, sizeof(descritor_sets) / sizeof(descritor_sets[0]), descritor_sets, sizeof(dynamic_offsets) / sizeof(dynamic_offsets[0]), dynamic_offsets); + + // TODO: support more vertex count + constexpr uint32_t const D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION = 65535U; + assert(scene_mesh_subset.m_vertex_count <= D3D11_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION); + command_buffer->dispatch(scene_mesh_subset.m_vertex_count, 1U, 1U); + } + } + } + } + + std::vector skinned_buffer_store_operations(skinned_buffers.size(), BRX_COMPUTE_PASS_STORAGE_BUFFER_STORE_OPERATION_FLUSH_FOR_READ_ONLY_STORAGE_BUFFER); + command_buffer->compute_pass_store(static_cast(skinned_buffers.size()), skinned_buffers.data(), skinned_buffer_store_operations.data(), 0U, NULL, NULL); + + command_buffer->end_debug_utils_label(); + } + } + + // GBuffer Pass + { + command_buffer->begin_debug_utils_label("GBuffer Pass"); + + float color_clear_values[4] = {0.0F, 0.0F, 0.0F, 0.0F}; + float depth_clear_value = 0.0F; + command_buffer->begin_render_pass(this->m_gbuffer_render_pass, this->m_gbuffer_frame_buffer, this->m_intermediate_width, this->m_intermediate_height, 1U, &color_clear_values, &depth_clear_value, NULL); + + command_buffer->bind_graphics_pipeline(this->m_gbuffer_pipeline); + + command_buffer->set_view_port(this->m_intermediate_width, this->m_intermediate_height); + + command_buffer->set_scissor(this->m_intermediate_width, this->m_intermediate_height); + + for (uint32_t mesh_index = 0; mesh_index < this->m_scene_meshes.size(); ++mesh_index) + { + Demo_Mesh const &scene_mesh = this->m_scene_meshes[mesh_index]; + + for (size_t subset_index = 0U; subset_index < scene_mesh.m_subsets.size(); ++subset_index) + { + Demo_Mesh_Subset const &scene_mesh_subset = scene_mesh.m_subsets[subset_index]; + + for (size_t instance_index = 0U; instance_index < scene_mesh.m_instances.size(); ++instance_index) + { + Demo_Mesh_Instance const &scene_mesh_instance = scene_mesh.m_instances[instance_index]; + + brx_descriptor_set *const descritor_sets[] = { + this->m_gbuffer_pipeline_none_update_descriptor_set, + (!scene_mesh.m_skinned) ? scene_mesh_subset.m_gbuffer_pipeline_per_mesh_subset_update_descriptor_set : scene_mesh_instance.m_subsets[subset_index].m_gbuffer_pipeline_per_mesh_skinned_subset_update_descriptor_set, + scene_mesh_instance.m_gbuffer_pipeline_per_mesh_instance_update_descriptor_set}; + + uint32_t const dynamic_offsets[] = { + tbb_align_up(static_cast(sizeof(common_none_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * frame_throttling_index, + tbb_align_up(static_cast(sizeof(gbuffer_pipeline_per_mesh_instance_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * frame_throttling_index}; + + command_buffer->bind_graphics_descriptor_sets(this->m_gbuffer_pipeline_layout, sizeof(descritor_sets) / sizeof(descritor_sets[0]), descritor_sets, sizeof(dynamic_offsets) / sizeof(dynamic_offsets[0]), dynamic_offsets); + + command_buffer->draw(scene_mesh_subset.m_index_count, 1U); + } + } + } + + command_buffer->end_render_pass(); + + command_buffer->end_debug_utils_label(); + } + + // Deferred Shading Pass + { + command_buffer->begin_debug_utils_label("Deferred Shading Pass"); + + float color_clear_values[4] = {0.0F, 0.0F, 0.0F, 0.0F}; + command_buffer->begin_render_pass(this->m_deferred_shading_render_pass, this->m_deferred_shading_frame_buffer, this->m_intermediate_width, this->m_intermediate_height, 1U, &color_clear_values, NULL, NULL); + + command_buffer->bind_graphics_pipeline(this->m_deferred_shading_pipeline); + + command_buffer->set_view_port(this->m_intermediate_width, this->m_intermediate_height); + + command_buffer->set_scissor(this->m_intermediate_width, this->m_intermediate_height); + + brx_descriptor_set *const descritor_sets[] = { + this->m_deferred_shading_pipeline_none_update_descriptor_set}; + + uint32_t const dynamic_offsets[] = { + tbb_align_up(static_cast(sizeof(common_none_update_set_uniform_buffer_binding)), this->m_uniform_upload_buffer_offset_alignment) * frame_throttling_index}; + + command_buffer->bind_graphics_descriptor_sets(this->m_deferred_shading_pipeline_layout, sizeof(descritor_sets) / sizeof(descritor_sets[0]), descritor_sets, sizeof(dynamic_offsets) / sizeof(dynamic_offsets[0]), dynamic_offsets); + + command_buffer->draw(3U, 1U); + + command_buffer->end_render_pass(); + + command_buffer->end_debug_utils_label(); + } +} + +static inline uint32_t tbb_align_up(uint32_t value, uint32_t alignment) +{ + // + // Copyright (c) 2005-2019 Intel Corporation + // + // Licensed under the Apache License, Version 2.0 (the "License"); + // you may not use this file except in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, software + // distributed under the License is distributed on an "AS IS" BASIS, + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + // See the License for the specific language governing permissions and + // limitations under the License. + // + + // [alignUp](https://github.com/oneapi-src/oneTBB/blob/tbb_2019/src/tbbmalloc/shared_utils.h#L42) + + assert(alignment != static_cast(0)); + + // power-of-2 alignment + assert((alignment & (alignment - static_cast(1))) == static_cast(0)); + + return (((value - static_cast(1)) | (alignment - static_cast(1))) + static_cast(1)); +} + +static inline DirectX::XMMATRIX XM_CALLCONV DirectX_Math_Matrix_PerspectiveFovRH_ReversedZ(float FovAngleY, float AspectRatio, float NearZ, float FarZ) +{ + // [Reversed-Z](https://developer.nvidia.com/content/depth-precision-visualized) + // + // _ 0 0 0 + // 0 _ 0 0 + // 0 0 b -1 + // 0 0 a 0 + // + // _ 0 0 0 + // 0 _ 0 0 + // 0 0 zb -z + // 0 0 a + // + // z' = -b - a/z + // + // Standard + // 0 = -b + a/nearz // z=-nearz + // 1 = -b + a/farz // z=-farz + // a = farz*nearz/(nearz - farz) + // b = farz/(nearz - farz) + // + // Reversed-Z + // 1 = -b + a/nearz // z=-nearz + // 0 = -b + a/farz // z=-farz + // a = farz*nearz/(farz - nearz) + // b = nearz/(farz - nearz) + + // __m128 _mm_shuffle_ps(__m128 lo,__m128 hi, _MM_SHUFFLE(hi3,hi2,lo1,lo0)) + // Interleave inputs into low 2 floats and high 2 floats of output. Basically + // out[0]=lo[lo0]; + // out[1]=lo[lo1]; + // out[2]=hi[hi2]; + // out[3]=hi[hi3]; + + // DirectX::XMMatrixPerspectiveFovRH + + float SinFov; + float CosFov; + DirectX::XMScalarSinCos(&SinFov, &CosFov, 0.5F * FovAngleY); + + float Height = CosFov / SinFov; + float Width = Height / AspectRatio; + float b = NearZ / (FarZ - NearZ); + float a = (FarZ / (FarZ - NearZ)) * NearZ; +#if defined(_XM_SSE_INTRINSICS_) + // Note: This is recorded on the stack + DirectX::XMVECTOR rMem = { + Width, + Height, + b, + a}; + + // Copy from memory to SSE register + DirectX::XMVECTOR vValues = rMem; + DirectX::XMVECTOR vTemp = _mm_setzero_ps(); + // Copy x only + vTemp = _mm_move_ss(vTemp, vValues); + // CosFov / SinFov,0,0,0 + DirectX::XMMATRIX M; + M.r[0] = vTemp; + // 0,Height / AspectRatio,0,0 + vTemp = vValues; + vTemp = _mm_and_ps(vTemp, DirectX::g_XMMaskY); + M.r[1] = vTemp; + // x=b,y=a,0,-1.0f + vTemp = _mm_setzero_ps(); + vValues = _mm_shuffle_ps(vValues, DirectX::g_XMNegIdentityR3, _MM_SHUFFLE(3, 2, 3, 2)); + // 0,0,b,-1.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 0, 0, 0)); + M.r[2] = vTemp; + // 0,0,a,0.0f + vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 1, 0, 0)); + M.r[3] = vTemp; + return M; +#elif defined(_XM_ARM_NEON_INTRINSICS_) + const float32x4_t Zero = vdupq_n_f32(0); + + DirectX::XMMATRIX M; + M.r[0] = vsetq_lane_f32(Width, Zero, 0); + M.r[1] = vsetq_lane_f32(Height, Zero, 1); + M.r[2] = vsetq_lane_f32(b, DirectX::g_XMNegIdentityR3.v, 2); + M.r[3] = vsetq_lane_f32(a, Zero, 2); + return M; +#endif +} diff --git a/source/demo.h b/source/demo.h new file mode 100644 index 0000000..1cd4a49 --- /dev/null +++ b/source/demo.h @@ -0,0 +1,128 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _DEMO_H_ +#define _DEMO_H_ 1 + +#include "support/frame_throttling.h" +#include "../thirdparty/Brioche/include/brx_device.h" +#include "../thirdparty/ImportAsset/include/import_scene_asset.h" +#include +#include + +struct Demo_Mesh_Skinned_Subset +{ + brx_storage_intermediate_buffer *m_skinned_vertex_position_buffer; + brx_storage_intermediate_buffer *m_skinned_vertex_varying_buffer; + brx_descriptor_set *m_skin_pipeline_per_mesh_skinned_subset_update_descriptor_set; + brx_descriptor_set *m_gbuffer_pipeline_per_mesh_skinned_subset_update_descriptor_set; +}; + +struct Demo_Mesh_Instance +{ + DirectX::XMFLOAT4X4 m_model_transform; + scene_animation_skeleton m_animation_skeleton; + std::vector m_subsets; + brx_uniform_upload_buffer *m_skin_pipeline_per_mesh_instance_update_uniform_buffer; + brx_descriptor_set *m_skin_pipeline_per_mesh_instance_update_descriptor_set; + brx_uniform_upload_buffer *m_gbuffer_pipeline_per_mesh_instance_update_uniform_buffer; + brx_descriptor_set *m_gbuffer_pipeline_per_mesh_instance_update_descriptor_set; +}; + +static constexpr uint32_t const DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT = 4U; + +struct Demo_Mesh_Subset +{ + uint32_t m_vertex_count; + brx_storage_asset_buffer *m_vertex_position_buffer; + brx_storage_asset_buffer *m_vertex_varying_buffer; + brx_storage_asset_buffer *m_vertex_joint_buffer; + uint32_t m_index_count; + brx_storage_asset_buffer *m_index_buffer; + brx_storage_asset_buffer *m_material_buffer; + brx_sampled_asset_image *m_material_textures[DEMO_MESH_SUBSET_METERIAL_TEXTURE_COUNT]; + brx_descriptor_set *m_gbuffer_pipeline_per_mesh_subset_update_descriptor_set; +}; + +struct Demo_Mesh +{ + bool m_skinned; + std::vector m_subsets; + std::vector m_instances; +}; + +class Demo +{ + brx_pipeline_layout *m_skin_pipeline_layout; + brx_compute_pipeline *m_skin_pipeline; + + BRX_COLOR_ATTACHMENT_IMAGE_FORMAT m_gbuffer_color_attachement_format; + BRX_DEPTH_STENCIL_ATTACHMENT_IMAGE_FORMAT m_gbuffer_depth_attachement_format; + brx_render_pass *m_gbuffer_render_pass; + + brx_pipeline_layout *m_gbuffer_pipeline_layout; + brx_graphics_pipeline *m_gbuffer_pipeline; + + // TODO: self dependency + // TODO: VK_EXT_rasterization_order_attachment_access + BRX_COLOR_ATTACHMENT_IMAGE_FORMAT m_deferred_shading_color_attachement_format; + brx_render_pass *m_deferred_shading_render_pass; + + brx_pipeline_layout *m_deferred_shading_pipeline_layout; + brx_graphics_pipeline *m_deferred_shading_pipeline; + + brx_sampled_asset_image *m_place_holder_texture; + + brx_sampler *m_sampler; + + uint32_t m_uniform_upload_buffer_offset_alignment; + brx_uniform_upload_buffer *m_common_none_update_uniform_buffer; + + brx_descriptor_set *m_gbuffer_pipeline_none_update_descriptor_set; + + brx_descriptor_set_layout *m_deferred_shading_pipeline_none_update_descriptor_set_layout; + brx_descriptor_set *m_deferred_shading_pipeline_none_update_descriptor_set; + + std::vector m_scene_meshes; + + uint32_t m_intermediate_width; + uint32_t m_intermediate_height; + brx_color_attachment_image *m_gbuffer_color_attachment; + brx_depth_stencil_attachment_image *m_gbuffer_depth_attachment; + brx_frame_buffer *m_gbuffer_frame_buffer; + brx_color_attachment_image *m_deferred_shading_color_attachment; + brx_frame_buffer *m_deferred_shading_frame_buffer; + + float m_animation_time; + +public: + Demo(); + + void init(brx_device *device, std::vector const &file_names); + + void destroy(brx_device *device); + + void on_swap_chain_attach(brx_device *device, uint32_t swap_chain_image_width, uint32_t swap_chain_image_height); + + brx_sampled_image const *get_sampled_image_for_present(); + + void on_swap_chain_dettach(brx_device *device); + + void draw(brx_graphics_command_buffer *command_buffer, float interval_time, uint32_t frame_throttling_index); +}; + +#endif \ No newline at end of file diff --git a/source/support/camera_controller.cpp b/source/support/camera_controller.cpp new file mode 100644 index 0000000..12a8cfd --- /dev/null +++ b/source/support/camera_controller.cpp @@ -0,0 +1,201 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include +#include +#include "camera_controller.h" + +class CameraController g_camera_controller; + +static float g_SpeedTranslate = 0.25F; +static float g_RotationTranslate = 2.0F; + +CameraController::CameraController() : m_previous_x(-1.0F), m_previous_y(-1.0F) +{ +} + +void CameraController::MoveForward() +{ + m_eye_position.x += m_eye_direction.x * g_SpeedTranslate; + m_eye_position.y += m_eye_direction.y * g_SpeedTranslate; + m_eye_position.z += m_eye_direction.z * g_SpeedTranslate; +} + +void CameraController::MoveBack() +{ + m_eye_position.x -= m_eye_direction.x * g_SpeedTranslate; + m_eye_position.y -= m_eye_direction.y * g_SpeedTranslate; + m_eye_position.z -= m_eye_direction.z * g_SpeedTranslate; +} + +void CameraController::MoveLeft() +{ + DirectX::XMFLOAT3 EyeDirection = {m_eye_direction.x, m_eye_direction.y, m_eye_direction.z}; + DirectX::XMFLOAT3 UpDirection = {m_up_direction.x, m_up_direction.y, m_up_direction.z}; + + DirectX::XMVECTOR NegEyeDirection = DirectX::XMVectorNegate(DirectX::XMLoadFloat3(&EyeDirection)); + DirectX::XMVECTOR AxisForwardDirection = DirectX::XMVector3Normalize(NegEyeDirection); + DirectX::XMVECTOR AxisRightDirection = DirectX::XMVector3Normalize(DirectX::XMVector3Cross(DirectX::XMLoadFloat3(&UpDirection), AxisForwardDirection)); + // DirectX::XMVECTOR AxisViewUpDirection = DirectX::XMVector3Cross(AxisForwardDirection, AxisRightDirection); + + DirectX::XMFLOAT3 AxisRightDirectionF; + DirectX::XMStoreFloat3(&AxisRightDirectionF, AxisRightDirection); + + m_eye_position.x -= AxisRightDirectionF.x * g_SpeedTranslate; + m_eye_position.y -= AxisRightDirectionF.y * g_SpeedTranslate; + m_eye_position.z -= AxisRightDirectionF.z * g_SpeedTranslate; +} + +void CameraController::MoveRight() +{ + DirectX::XMFLOAT3 EyeDirection = {m_eye_direction.x, m_eye_direction.y, m_eye_direction.z}; + DirectX::XMFLOAT3 UpDirection = {m_up_direction.x, m_up_direction.y, m_up_direction.z}; + + DirectX::XMVECTOR NegEyeDirection = DirectX::XMVectorNegate(DirectX::XMLoadFloat3(&EyeDirection)); + DirectX::XMVECTOR AxisForwardDirection = DirectX::XMVector3Normalize(NegEyeDirection); + DirectX::XMVECTOR AxisRightDirection = DirectX::XMVector3Normalize(DirectX::XMVector3Cross(DirectX::XMLoadFloat3(&UpDirection), AxisForwardDirection)); + // DirectX::XMVECTOR AxisViewUpDirection = DirectX::XMVector3Cross(AxisForwardDirection, AxisRightDirection); + + DirectX::XMFLOAT3 AxisRightDirectionF; + DirectX::XMStoreFloat3(&AxisRightDirectionF, AxisRightDirection); + + m_eye_position.x += AxisRightDirectionF.x * g_SpeedTranslate; + m_eye_position.y += AxisRightDirectionF.y * g_SpeedTranslate; + m_eye_position.z += AxisRightDirectionF.z * g_SpeedTranslate; +} + +void CameraController::MoveUp() +{ + DirectX::XMFLOAT3 EyeDirection = {m_eye_direction.x, m_eye_direction.y, m_eye_direction.z}; + DirectX::XMFLOAT3 UpDirection = {m_up_direction.x, m_up_direction.y, m_up_direction.z}; + + DirectX::XMVECTOR NegEyeDirection = DirectX::XMVectorNegate(DirectX::XMLoadFloat3(&EyeDirection)); + DirectX::XMVECTOR AxisForwardDirection = DirectX::XMVector3Normalize(NegEyeDirection); + DirectX::XMVECTOR AxisRightDirection = DirectX::XMVector3Normalize(DirectX::XMVector3Cross(DirectX::XMLoadFloat3(&UpDirection), AxisForwardDirection)); + DirectX::XMVECTOR AxisViewUpDirection = DirectX::XMVector3Cross(AxisForwardDirection, AxisRightDirection); + + DirectX::XMFLOAT3 AxisViewUpDirectionF; + DirectX::XMStoreFloat3(&AxisViewUpDirectionF, AxisViewUpDirection); + + m_eye_position.x += AxisViewUpDirectionF.x * g_SpeedTranslate; + m_eye_position.y += AxisViewUpDirectionF.y * g_SpeedTranslate; + m_eye_position.z += AxisViewUpDirectionF.z * g_SpeedTranslate; +} + +void CameraController::MoveDown() +{ + DirectX::XMFLOAT3 EyeDirection = {m_eye_direction.x, m_eye_direction.y, m_eye_direction.z}; + DirectX::XMFLOAT3 UpDirection = {m_up_direction.x, m_up_direction.y, m_up_direction.z}; + + DirectX::XMVECTOR NegEyeDirection = DirectX::XMVectorNegate(DirectX::XMLoadFloat3(&EyeDirection)); + DirectX::XMVECTOR AxisForwardDirection = DirectX::XMVector3Normalize(NegEyeDirection); + DirectX::XMVECTOR AxisRightDirection = DirectX::XMVector3Normalize(DirectX::XMVector3Cross(DirectX::XMLoadFloat3(&UpDirection), AxisForwardDirection)); + DirectX::XMVECTOR AxisViewUpDirection = DirectX::XMVector3Cross(AxisForwardDirection, AxisRightDirection); + + DirectX::XMFLOAT3 AxisViewUpDirectionF; + DirectX::XMStoreFloat3(&AxisViewUpDirectionF, AxisViewUpDirection); + + m_eye_position.x -= AxisViewUpDirectionF.x * g_SpeedTranslate; + m_eye_position.y -= AxisViewUpDirectionF.y * g_SpeedTranslate; + m_eye_position.z -= AxisViewUpDirectionF.z * g_SpeedTranslate; +} + +void CameraController::OnMouseMove(float x, float y, bool hold) +{ + float Current_X = std::min(std::max(0.0f, x), 1.0f); + float Current_Y = std::min(std::max(0.0f, y), 1.0f); + + float Offset_X = Current_X - this->m_previous_x; + float Offset_Y = Current_Y - this->m_previous_y; + + if (hold && this->m_previous_x > 0.0F && this->m_previous_x < 1.0F && this->m_previous_y > 0.0F && this->m_previous_y < 1.0F) + { + float LengthNormalized = ::sqrtf(Offset_X * Offset_X + Offset_Y * Offset_Y); + + if ((Offset_Y < Offset_X) && (Offset_Y > -Offset_X)) + { + // right + DirectX::XMFLOAT3 EyeDirection = {m_eye_direction.x, m_eye_direction.y, m_eye_direction.z}; + DirectX::XMFLOAT3 UpDirection = {m_up_direction.x, m_up_direction.y, m_up_direction.z}; + + DirectX::XMVECTOR NegEyeDirection = DirectX::XMVectorNegate(DirectX::XMLoadFloat3(&EyeDirection)); + DirectX::XMVECTOR AxisForwardDirection = DirectX::XMVector3Normalize(NegEyeDirection); + DirectX::XMVECTOR AxisRightDirection = DirectX::XMVector3Normalize(DirectX::XMVector3Cross(DirectX::XMLoadFloat3(&UpDirection), AxisForwardDirection)); + DirectX::XMVECTOR AxisViewUpDirection = DirectX::XMVector3Cross(AxisForwardDirection, AxisRightDirection); + + DirectX::XMFLOAT3 EyeDirectionNew; + DirectX::XMStoreFloat3(&EyeDirectionNew, DirectX::XMVectorNegate(DirectX::XMVector3Transform(AxisForwardDirection, DirectX::XMMatrixRotationAxis(AxisViewUpDirection, -g_RotationTranslate * LengthNormalized)))); + m_eye_direction.x = EyeDirectionNew.x; + m_eye_direction.y = EyeDirectionNew.y; + m_eye_direction.z = EyeDirectionNew.z; + } + else if ((Offset_Y < -Offset_X) && (Offset_Y > Offset_X)) + { + // left + DirectX::XMFLOAT3 EyeDirection = {m_eye_direction.x, m_eye_direction.y, m_eye_direction.z}; + DirectX::XMFLOAT3 UpDirection = {m_up_direction.x, m_up_direction.y, m_up_direction.z}; + + DirectX::XMVECTOR NegEyeDirection = DirectX::XMVectorNegate(DirectX::XMLoadFloat3(&EyeDirection)); + DirectX::XMVECTOR AxisForwardDirection = DirectX::XMVector3Normalize(NegEyeDirection); + DirectX::XMVECTOR AxisRightDirection = DirectX::XMVector3Normalize(DirectX::XMVector3Cross(DirectX::XMLoadFloat3(&UpDirection), AxisForwardDirection)); + DirectX::XMVECTOR AxisViewUpDirection = DirectX::XMVector3Cross(AxisForwardDirection, AxisRightDirection); + + DirectX::XMFLOAT3 EyeDirectionNew; + DirectX::XMStoreFloat3(&EyeDirectionNew, DirectX::XMVectorNegate(DirectX::XMVector3Transform(AxisForwardDirection, DirectX::XMMatrixRotationAxis(AxisViewUpDirection, g_RotationTranslate * LengthNormalized)))); + m_eye_direction.x = EyeDirectionNew.x; + m_eye_direction.y = EyeDirectionNew.y; + m_eye_direction.z = EyeDirectionNew.z; + } + else if ((Offset_Y < Offset_X) && (Offset_Y < -Offset_X)) + { + // up + DirectX::XMFLOAT3 EyeDirection = {m_eye_direction.x, m_eye_direction.y, m_eye_direction.z}; + DirectX::XMFLOAT3 UpDirection = {m_up_direction.x, m_up_direction.y, m_up_direction.z}; + + DirectX::XMVECTOR NegEyeDirection = DirectX::XMVectorNegate(DirectX::XMLoadFloat3(&EyeDirection)); + DirectX::XMVECTOR AxisForwardDirection = DirectX::XMVector3Normalize(NegEyeDirection); + DirectX::XMVECTOR AxisRightDirection = DirectX::XMVector3Normalize(DirectX::XMVector3Cross(DirectX::XMLoadFloat3(&UpDirection), AxisForwardDirection)); + // DirectX::XMVECTOR AxisViewUpDirection = DirectX::XMVector3Cross(AxisForwardDirection, AxisRightDirection); + + DirectX::XMFLOAT3 EyeDirectionNew; + DirectX::XMStoreFloat3(&EyeDirectionNew, DirectX::XMVectorNegate(DirectX::XMVector3Transform(AxisForwardDirection, DirectX::XMMatrixRotationAxis(AxisRightDirection, g_RotationTranslate * LengthNormalized)))); + m_eye_direction.x = EyeDirectionNew.x; + m_eye_direction.y = EyeDirectionNew.y; + m_eye_direction.z = EyeDirectionNew.z; + } + else if ((Offset_Y > Offset_X) && (Offset_Y > -Offset_X)) + { + // down + DirectX::XMFLOAT3 EyeDirection = {m_eye_direction.x, m_eye_direction.y, m_eye_direction.z}; + DirectX::XMFLOAT3 UpDirection = {m_up_direction.x, m_up_direction.y, m_up_direction.z}; + + DirectX::XMVECTOR NegEyeDirection = DirectX::XMVectorNegate(DirectX::XMLoadFloat3(&EyeDirection)); + DirectX::XMVECTOR AxisForwardDirection = DirectX::XMVector3Normalize(NegEyeDirection); + DirectX::XMVECTOR AxisRightDirection = DirectX::XMVector3Normalize(DirectX::XMVector3Cross(DirectX::XMLoadFloat3(&UpDirection), AxisForwardDirection)); + // DirectX::XMVECTOR AxisViewUpDirection = DirectX::XMVector3Cross(AxisForwardDirection, AxisRightDirection); + + DirectX::XMFLOAT3 EyeDirectionNew; + DirectX::XMStoreFloat3(&EyeDirectionNew, DirectX::XMVectorNegate(DirectX::XMVector3Transform(AxisForwardDirection, DirectX::XMMatrixRotationAxis(AxisRightDirection, -g_RotationTranslate * LengthNormalized)))); + m_eye_direction.x = EyeDirectionNew.x; + m_eye_direction.y = EyeDirectionNew.y; + m_eye_direction.z = EyeDirectionNew.z; + } + } + + this->m_previous_x = Current_X; + this->m_previous_y = Current_Y; +} diff --git a/source/support/camera_controller.h b/source/support/camera_controller.h new file mode 100644 index 0000000..c6f2263 --- /dev/null +++ b/source/support/camera_controller.h @@ -0,0 +1,54 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _CAMERA_CONTROLLER_H_ +#define _CAMERA_CONTROLLER_H_ 1 + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#endif +#include +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +class CameraController +{ + float m_previous_x; + float m_previous_y; + +public: + DirectX::XMFLOAT3 m_eye_position; + DirectX::XMFLOAT3 m_eye_direction; + DirectX::XMFLOAT3 m_up_direction; + + CameraController(); + + void MoveForward(); + void MoveBack(); + void MoveLeft(); + void MoveRight(); + void MoveUp(); + void MoveDown(); + + void OnMouseMove(float x, float y, bool hold); +}; + +extern class CameraController g_camera_controller; + +#endif \ No newline at end of file diff --git a/source/support/frame_throttling.h b/source/support/frame_throttling.h new file mode 100644 index 0000000..44256b0 --- /dev/null +++ b/source/support/frame_throttling.h @@ -0,0 +1,26 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _FRAME_THROTTLINE_H_ +#define _FRAME_THROTTLINE_H_ 1 + +#include + +// [Pipeline Throttling](https://community.arm.com/arm-community-blogs/b/graphics-gaming-and-vr-blog/posts/the-mali-gpu-an-abstract-machine-part-1---frame-pipelining) +static uint32_t constexpr const FRAME_THROTTLING_COUNT = 3U; + +#endif \ No newline at end of file diff --git a/source/support/main.cpp b/source/support/main.cpp new file mode 100644 index 0000000..7d66156 --- /dev/null +++ b/source/support/main.cpp @@ -0,0 +1,957 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include +#include +#include "camera_controller.h" +#include "renderer.h" + +static class renderer *g_renderer = NULL; +static int32_t g_window_width = -1; +static int32_t g_window_height = -1; + +#if defined(__GNUC__) + +#if defined(__linux__) + +#if defined(__ANDROID__) +#include +#include +#include +#include +#include +#include +#include +#include + +static void ANativeActivity_Destroy(ANativeActivity *native_activity); +static void ANativeActivity_WindowFocusChanged(ANativeActivity *native_activity, int hasFocus); +static void ANativeActivity_NativeWindowCreated(ANativeActivity *native_activity, ANativeWindow *native_window); +static void ANativeActivity_NativeWindowResized(ANativeActivity *native_activity, ANativeWindow *native_window); +static void ANativeActivity_NativeWindowRedrawNeeded(ANativeActivity *native_activity, ANativeWindow *native_window); +static void ANativeActivity_NativeWindowDestroyed(ANativeActivity *native_activity, ANativeWindow *native_window); +static void ANativeActivity_InputQueueCreated(ANativeActivity *native_activity, AInputQueue *input_queue); +static void ANativeActivity_InputQueueDestroyed(ANativeActivity *native_activity, AInputQueue *input_queue); + +static bool g_this_process_has_inited = false; + +static int g_main_thread_looper_draw_callback_fd_read = -1; +static int g_main_thread_looper_draw_callback_fd_write = -1; + +static int main_thread_looper_draw_callback(int fd, int, void *); + +static pthread_t g_draw_request_thread; +bool volatile g_draw_request_thread_running = false; + +static void *draw_request_thread_main(void *); + +static int main_thread_looper_input_queue_callback(int fd, int, void *input_queue_void); + +extern "C" JNIEXPORT void ANativeActivity_onCreate(ANativeActivity *native_activity, void *saved_state, size_t saved_state_size) +{ + native_activity->callbacks->onStart = NULL; + native_activity->callbacks->onResume = NULL; + native_activity->callbacks->onSaveInstanceState = NULL; + native_activity->callbacks->onPause = NULL; + native_activity->callbacks->onStop = NULL; + native_activity->callbacks->onDestroy = ANativeActivity_Destroy; + native_activity->callbacks->onWindowFocusChanged = ANativeActivity_WindowFocusChanged; + native_activity->callbacks->onNativeWindowCreated = ANativeActivity_NativeWindowCreated; + native_activity->callbacks->onNativeWindowResized = ANativeActivity_NativeWindowResized; + native_activity->callbacks->onNativeWindowRedrawNeeded = ANativeActivity_NativeWindowRedrawNeeded; + native_activity->callbacks->onNativeWindowDestroyed = ANativeActivity_NativeWindowDestroyed; + native_activity->callbacks->onInputQueueCreated = ANativeActivity_InputQueueCreated; + native_activity->callbacks->onInputQueueDestroyed = ANativeActivity_InputQueueDestroyed; + native_activity->callbacks->onContentRectChanged = NULL; + native_activity->callbacks->onConfigurationChanged = NULL; + native_activity->callbacks->onLowMemory = NULL; + + if (!g_this_process_has_inited) + { + // TODO: get file name + std::vector file_names; + + g_renderer = renderer_init(NULL, file_names); + + // Simulate the following callback on Android: + // WM_PAINT + // CVDisplayLinkSetOutputCallback + // displayLinkWithTarget + { + // the looper of the main thread + ALooper *main_thread_looper = ALooper_forThread(); + assert(main_thread_looper != NULL); + + // Evidently, the real "draw" is slower than the "request" + // There are no message boundaries for "SOCK_STREAM", and We can read all the data once + int sv[2]; + int res_socketpair = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); + assert(0 == res_socketpair); + g_main_thread_looper_draw_callback_fd_read = sv[0]; + g_main_thread_looper_draw_callback_fd_write = sv[1]; + + // the identifier is ignored when callback is not NULL (the ALooper_pollOnce always returns the ALOOPER_POLL_CALLBACK) + int res_looper_add_fd = ALooper_addFd(main_thread_looper, g_main_thread_looper_draw_callback_fd_read, -1, ALOOPER_EVENT_INPUT, main_thread_looper_draw_callback, NULL); + assert(1 == res_looper_add_fd); + + // the "draw request" thread + // TODO: use Load/Store + g_draw_request_thread_running = false; + int res_ptread_create = pthread_create(&g_draw_request_thread, NULL, draw_request_thread_main, NULL); + assert(0 == res_ptread_create); + while (!g_draw_request_thread_running) + { + // pthread_yield(); + sched_yield(); + } + } + + // Don't worry + // Single thread + g_this_process_has_inited = true; + } +} + +// TODO: +// destroy before this process terminates + +static void ANativeActivity_Destroy(ANativeActivity *native_activity) +{ +} + +static void ANativeActivity_WindowFocusChanged(ANativeActivity *native_activity, int hasFocus) +{ +} + +static void ANativeActivity_NativeWindowCreated(ANativeActivity *native_activity, ANativeWindow *native_window) +{ + g_window_width = ANativeWindow_getWidth(native_window); + g_window_height = ANativeWindow_getHeight(native_window); + + renderer_attach_window(g_renderer, native_window); +} + +static void ANativeActivity_NativeWindowResized(ANativeActivity *native_activity, ANativeWindow *native_window) +{ +} + +static void ANativeActivity_NativeWindowRedrawNeeded(ANativeActivity *native_activity, ANativeWindow *native_window) +{ +} + +static void ANativeActivity_NativeWindowDestroyed(ANativeActivity *native_activity, ANativeWindow *native_window) +{ + renderer_dettach_window(g_renderer); + + g_window_width = -1; + g_window_height = -1; +} + +static void ANativeActivity_InputQueueCreated(ANativeActivity *native_activity, AInputQueue *input_queue) +{ + // the looper of the main thread + ALooper *main_thread_looper = ALooper_forThread(); + assert(NULL != main_thread_looper); + + // the identifier is ignored when callback is not NULL (the ALooper_pollOnce always returns the ALOOPER_POLL_CALLBACK) + AInputQueue_attachLooper(input_queue, main_thread_looper, 0, main_thread_looper_input_queue_callback, input_queue); +} + +static void ANativeActivity_InputQueueDestroyed(ANativeActivity *native_activity, AInputQueue *input_queue) +{ + ALooper *looper = ALooper_forThread(); + assert(NULL != looper); + + AInputQueue_detachLooper(input_queue); +} + +static int main_thread_looper_draw_callback(int fd, int, void *) +{ + // Evidently, the real "draw" is slower than the "request" + // There are no message boundaries for "SOCK_STREAM", and We can read all the data once + { + uint8_t buf[4096]; + ssize_t res_recv; + while ((-1 == (res_recv = recv(fd, buf, 4096U, 0))) && (EINTR == errno)) + { + // pthread_yield(); + sched_yield(); + } + assert(-1 != res_recv); + } + + // draw + renderer_draw(g_renderer); + + return 1; +} + +static void *draw_request_thread_main(void *) +{ + g_draw_request_thread_running = true; + + while (g_draw_request_thread_running) + { + // 60 FPS + uint32_t milli_second = 1000U / 60U; + + // wait + { + struct timespec request = {((time_t)milli_second) / ((time_t)1000), ((long)1000000) * (((long)milli_second) % ((long)1000))}; + + struct timespec remain; + int res_nanosleep; + while ((-1 == (res_nanosleep = nanosleep(&request, &remain))) && (EINTR == errno)) + { + assert(remain.tv_nsec > 0 || remain.tv_sec > 0); + request = remain; + } + assert(0 == res_nanosleep); + } + + // draw request + { + uint8_t buf[1] = {7}; // seven is the luck number + ssize_t res_send; + while ((-1 == (res_send = send(g_main_thread_looper_draw_callback_fd_write, buf, 1U, 0))) && (EINTR == errno)) + { + // pthread_yield(); + sched_yield(); + } + assert(1 == res_send); + } + } + + return NULL; +} + +static int main_thread_looper_input_queue_callback(int fd, int, void *input_queue_void) +{ + AInputQueue *input_queue = static_cast(input_queue_void); + + AInputEvent *input_event; + while (AInputQueue_getEvent(input_queue, &input_event) >= 0) + { + // The app will be "No response" if we don't call AInputQueue_finishEvent and pass the non-zero value for all events which is not pre-dispatched + if (0 == AInputQueue_preDispatchEvent(input_queue, input_event)) + { + int handled = 0; + + switch (AInputEvent_getType(input_event)) + { + case AINPUT_EVENT_TYPE_MOTION: + { + + float Current_X = AMotionEvent_getX(input_event, 0U); + float Current_Y = AMotionEvent_getY(input_event, 0U); + + float CurrentNormalized_X = Current_X / static_cast(g_window_width); + float CurrentNormalized_Y = Current_Y / static_cast(g_window_height); + + switch (AMotionEvent_getAction(input_event) & AMOTION_EVENT_ACTION_MASK) + { + case AMOTION_EVENT_ACTION_DOWN: + { + g_camera_controller.OnMouseMove(CurrentNormalized_X, CurrentNormalized_Y, false); + } + break; + case AMOTION_EVENT_ACTION_UP: + { + g_camera_controller.OnMouseMove(CurrentNormalized_X, CurrentNormalized_Y, false); + } + break; + case AMOTION_EVENT_ACTION_MOVE: + { + g_camera_controller.OnMouseMove(CurrentNormalized_X, CurrentNormalized_Y, true); + } + break; + default: + { + // Do Nothing + } + } + } + break; + default: + { + // Do Nothing + } + } + + AInputQueue_finishEvent(input_queue, input_event, handled); + } + } + + return 1; +} +#else + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // Vulkan Validation Layer +#ifndef NDEBUG + { + // We assume that the "VkLayer_khronos_validation.json" is at the same directory of the executable file + char dir_name[4096]; + ssize_t res_read_link = readlink("/proc/self/exe", dir_name, sizeof(dir_name) / sizeof(dir_name[0])); + assert(-1 != res_read_link); + + for (int i = (res_read_link - 1); i > 0; --i) + { + if (L'/' == dir_name[i]) + { + dir_name[i] = L'\0'; + break; + } + } + + int res_set_env_vk_layer_path = setenv("VK_LAYER_PATH", dir_name, 1); + assert(0 == res_set_env_vk_layer_path); + + int res_set_env_ld_library_path = setenv("LD_LIBRARY_PATH", dir_name, 1); + assert(0 == res_set_env_ld_library_path); + } +#endif + + std::vector file_names; + { + if (argc < 2) + { + puts("Usage: glTF-Viewer \n"); + return 0; + } + else + { + file_names.resize(static_cast(argc - 1)); + + for (int arg_index = 1; arg_index < argc; ++arg_index) + { + file_names[arg_index - 1] = argv[arg_index]; + } + } + } + + g_window_width = 1280; + g_window_height = 720; + + xcb_connection_t *connection = NULL; + xcb_screen_t *screen = NULL; + { + int screen_number; + connection = xcb_connect(NULL, &screen_number); + assert(0 == xcb_connection_has_error(connection)); + + xcb_setup_t const *setup = xcb_get_setup(connection); + + int i = 0; + for (xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(setup); screen_iterator.rem > 0; xcb_screen_next(&screen_iterator)) + { + if (i == screen_number) + { + screen = screen_iterator.data; + break; + } + ++i; + } + } + + constexpr uint8_t const depth = 32; + xcb_visualid_t visual_id = -1; + { + for (xcb_depth_iterator_t depth_iterator = xcb_screen_allowed_depths_iterator(screen); depth_iterator.rem > 0; xcb_depth_next(&depth_iterator)) + { + if (depth == depth_iterator.data->depth) + { + for (xcb_visualtype_iterator_t visual_iterator = xcb_depth_visuals_iterator(depth_iterator.data); visual_iterator.rem > 0; xcb_visualtype_next(&visual_iterator)) + { + if (XCB_VISUAL_CLASS_TRUE_COLOR == visual_iterator.data->_class) + { + visual_id = visual_iterator.data->visual_id; + break; + } + } + break; + } + } + } + + xcb_colormap_t colormap = 0; + { + colormap = xcb_generate_id(connection); + + xcb_void_cookie_t cookie_create_colormap = xcb_create_colormap_checked(connection, XCB_COLORMAP_ALLOC_NONE, colormap, screen->root, visual_id); + + xcb_generic_error_t *error_create_colormap = xcb_request_check(connection, cookie_create_colormap); + assert(NULL == error_create_colormap); + } + + xcb_window_t window = 0; + { + window = xcb_generate_id(connection); + + // Both "border pixel" and "colormap" are required when the depth is NOT equal to the root window's. + uint32_t value_mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_BACKING_STORE | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; + + // we need "XCB_EVENT_MASK_BUTTON_PRESS" and "XCB_EVENT_MASK_BUTTON_RELEASE" to use the "state" of the "motion notify" event + uint32_t value_list[] = {screen->black_pixel, 0, XCB_BACKING_STORE_NOT_USEFUL, XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_STRUCTURE_NOTIFY, colormap}; + + xcb_void_cookie_t cookie_create_window = xcb_create_window_checked(connection, depth, window, screen->root, 0, 0, g_window_width, g_window_height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visual_id, value_mask, value_list); + + xcb_generic_error_t *error_create_window = xcb_request_check(connection, cookie_create_window); + assert(NULL == error_create_window); + } + + xcb_atom_t atom_wm_protocols = 0; + xcb_atom_t atom_wm_delete_window = 0; + { + xcb_intern_atom_cookie_t cookie_wm_protocols = xcb_intern_atom(connection, 0, 12U, "WM_PROTOCOLS"); + + xcb_intern_atom_cookie_t cookie_wm_delete_window = xcb_intern_atom(connection, 0, 16U, "WM_DELETE_WINDOW"); + + xcb_generic_error_t *error_intern_atom_reply_wm_protocols; + xcb_intern_atom_reply_t *reply_wm_protocols = xcb_intern_atom_reply(connection, cookie_wm_protocols, &error_intern_atom_reply_wm_protocols); + assert(NULL == error_intern_atom_reply_wm_protocols); + atom_wm_protocols = reply_wm_protocols->atom; + free(error_intern_atom_reply_wm_protocols); + + xcb_generic_error_t *error_intern_atom_reply_wm_delete_window; + xcb_intern_atom_reply_t *reply_wm_delete_window = xcb_intern_atom_reply(connection, cookie_wm_delete_window, &error_intern_atom_reply_wm_delete_window); + assert(NULL == error_intern_atom_reply_wm_delete_window); + atom_wm_delete_window = reply_wm_delete_window->atom; + free(error_intern_atom_reply_wm_delete_window); + } + + { + xcb_void_cookie_t cookie_change_property_wm_name = xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8 * sizeof(uint8_t), 11, "glTF Viewer"); + + xcb_void_cookie_t cookie_change_property_wm_protocols_delete_window = xcb_change_property_checked(connection, XCB_PROP_MODE_REPLACE, window, atom_wm_protocols, XCB_ATOM_ATOM, 8 * sizeof(uint32_t), sizeof(xcb_atom_t) / sizeof(uint32_t), &atom_wm_delete_window); + + xcb_generic_error_t *error_change_property_net_wm_name = xcb_request_check(connection, cookie_change_property_wm_name); + assert(NULL == error_change_property_net_wm_name); + + xcb_generic_error_t *error_change_property_wm_protocols_delete_window = xcb_request_check(connection, cookie_change_property_wm_protocols_delete_window); + assert(NULL == error_change_property_wm_protocols_delete_window); + } + + { + struct brx_xcb_connection_T + { + xcb_connection_t *m_connection; + xcb_visualid_t m_visual_id; + }; + brx_xcb_connection_T brx_xcb_connection = { + connection, + visual_id}; + + g_renderer = renderer_init(&brx_xcb_connection, file_names); + + struct brx_xcb_window_T + { + xcb_connection_t *m_connection; + xcb_window_t m_window; + }; + + brx_xcb_window_T brx_xcb_window = { + connection, + window}; + + renderer_attach_window(g_renderer, &brx_xcb_window); + } + + { + xcb_void_cookie_t cookie_map_window = xcb_map_window_checked(connection, window); + + xcb_generic_error_t *error_map_window = xcb_request_check(connection, cookie_map_window); + assert(NULL == error_map_window); + } + + bool quit = false; + while (!quit) + { + // WSI Event + { + xcb_generic_event_t *event; + while ((event = xcb_poll_for_event(connection)) != NULL) + { + // The most significant bit(uint8_t(0X80)) in this code is set if the event was generated from a SendEvent request. + // https://www.x.org/releases/current/doc/xproto/x11protocol.html#event_format + switch (event->response_type & (~uint8_t(0X80))) + { + case XCB_KEY_PRESS: + { + assert(XCB_KEY_PRESS == (event->response_type & (~uint8_t(0X80)))); + + xcb_key_press_event_t const *const key_press = reinterpret_cast(event); + + // evdev keycode + // https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/blob/master/keycodes/evdev + // Q + constexpr xcb_keycode_t const AD01 = 24; + // W + constexpr xcb_keycode_t const AD02 = 25; + // E + constexpr xcb_keycode_t const AD03 = 26; + // A + constexpr xcb_keycode_t const AC01 = 38; + // S + constexpr xcb_keycode_t const AC02 = 39; + // D + constexpr xcb_keycode_t const AC03 = 40; + + switch (key_press->detail) + { + case AD02: + { + g_camera_controller.MoveForward(); + } + break; + case AC02: + { + g_camera_controller.MoveBack(); + } + break; + case AC01: + { + g_camera_controller.MoveLeft(); + } + break; + case AC03: + { + g_camera_controller.MoveRight(); + } + break; + case AD01: + { + g_camera_controller.MoveDown(); + } + break; + case AD03: + { + g_camera_controller.MoveUp(); + } + break; + } + } + break; + case XCB_MOTION_NOTIFY: + { + assert(XCB_MOTION_NOTIFY == (event->response_type & (~uint8_t(0X80)))); + + xcb_motion_notify_event_t const *const motion_notify = reinterpret_cast(event); + + bool right_button_hold = (0U != (motion_notify->state & XCB_EVENT_MASK_BUTTON_3_MOTION)); + + float current_normalized_x = static_cast(motion_notify->event_x) / g_window_width; + float current_normalized_y = static_cast(motion_notify->event_y) / g_window_height; + + g_camera_controller.OnMouseMove(current_normalized_x, current_normalized_y, right_button_hold); + } + break; + case XCB_CONFIGURE_NOTIFY: + { + assert(XCB_CONFIGURE_NOTIFY == (event->response_type & (~uint8_t(0X80)))); + + xcb_configure_notify_event_t const *const configure_notify = reinterpret_cast(event); + + if (g_window_width != configure_notify->width || g_window_height != configure_notify->height) + { + renderer_on_window_resize(g_renderer); + g_window_width = configure_notify->width; + g_window_height = configure_notify->height; + } + } + break; + case XCB_CLIENT_MESSAGE: + { + assert(XCB_CLIENT_MESSAGE == (event->response_type & (~uint8_t(0X80)))); + + xcb_client_message_event_t const *const client_message_event = reinterpret_cast(event); + assert(client_message_event->type == atom_wm_protocols && client_message_event->data.data32[0] == atom_wm_delete_window && client_message_event->window == window); + + quit = true; + } + break; + case 0: + { + assert(0 == (event->response_type & (~uint8_t(0X80)))); + + xcb_generic_error_t const *error = reinterpret_cast(event); + + printf("Error Code: %d Major Code: %d", static_cast(error->error_code), static_cast(error->major_code)); + } + break; + } + + free(event); + } + } + + // Render + renderer_draw(g_renderer); + } + + { + xcb_void_cookie_t cookie_free_colormap = xcb_free_colormap_checked(connection, colormap); + + xcb_generic_error_t *error_free_colormap = xcb_request_check(connection, cookie_free_colormap); + assert(NULL == error_free_colormap); + } + + xcb_disconnect(connection); + + renderer_dettach_window(g_renderer); + + renderer_destroy(g_renderer); + + return 0; +} + +#endif +#else +#error Unknown Platform +#endif + +#elif defined(_MSC_VER) + +#define NOMINMAX 1 +#define WIN32_LEAN_AND_MEAN 1 +#include +#include +#include +#include +#include +#include "../../thirdparty/ConvertUTF/include/ConvertUTF.h" + +static LRESULT CALLBACK wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +static HRESULT STDMETHODCALLTYPE NoRegCoCreate(const __wchar_t *dllName, REFCLSID rclsid, REFIID riid, void **ppv); + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +int wmain(int argc, wchar_t *argv[]) +{ + // Vulkan Validation Layer +#ifndef NDEBUG + { + // We assume that the "VkLayer_khronos_validation.json" is at the same directory of the executable file + WCHAR file_name[4096]; + DWORD res_get_file_name = GetModuleFileNameW(NULL, file_name, sizeof(file_name) / sizeof(file_name[0])); + assert(0U != res_get_file_name); + + for (int i = (res_get_file_name - 1); i > 0; --i) + { + if (L'\\' == file_name[i]) + { + file_name[i] = L'\0'; + break; + } + } + + BOOL res_set_environment_variable = SetEnvironmentVariableW(L"VK_LAYER_PATH", file_name); + assert(FALSE != res_set_environment_variable); + } +#endif + + // Initialize + { + std::vector file_names; + { + if (argc < 2) + { + HRESULT res_co_initialize_ex = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + assert(SUCCEEDED(res_co_initialize_ex)); + + IFileOpenDialog *file_open_dialog = NULL; + // HRESULT res_co_create_instance = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&file_open_dialog)); + HRESULT res_co_create_instance = (NULL != GetOpenFileNameW) ? NoRegCoCreate(L"Comdlg32.dll", CLSID_FileOpenDialog, IID_PPV_ARGS(&file_open_dialog)) : E_FAIL; + assert(SUCCEEDED(res_co_create_instance)); + + FILEOPENDIALOGOPTIONS file_open_dialog_options; + HRESULT res_file_open_dialog_get_options = file_open_dialog->GetOptions(&file_open_dialog_options); + assert(SUCCEEDED(res_file_open_dialog_get_options)); + + file_open_dialog_options |= FOS_FORCEFILESYSTEM; + file_open_dialog_options |= FOS_ALLOWMULTISELECT; + + HRESULT res_file_open_dialog_set_options = file_open_dialog->SetOptions(file_open_dialog_options); + assert(SUCCEEDED(res_file_open_dialog_set_options)); + + COMDLG_FILTERSPEC const filter_specs[] = { + {L"All Files", L"*.*"}, + {L"glTF Binary", L"*.glb"}, + {L"glTF Separate", L"*.gltf"}}; + HRESULT res_file_open_dialog_set_file_types = file_open_dialog->SetFileTypes(sizeof(filter_specs) / sizeof(filter_specs[0]), filter_specs); + assert(SUCCEEDED(res_file_open_dialog_set_file_types)); + + HRESULT res_file_open_dialog_set_file_type_index = file_open_dialog->SetFileTypeIndex(3U); + assert(SUCCEEDED(res_file_open_dialog_set_file_type_index)); + + HRESULT res_file_open_dialog_show = file_open_dialog->Show(NULL); + if (SUCCEEDED(res_file_open_dialog_show)) + { + IShellItemArray *item_array; + HRESULT res_file_open_dialog_get_result = file_open_dialog->GetResults(&item_array); + assert(SUCCEEDED(res_file_open_dialog_get_result)); + + DWORD item_count; + HRESULT res_shell_item_array_get_count = item_array->GetCount(&item_count); + assert(SUCCEEDED(res_shell_item_array_get_count)); + + file_names.resize(static_cast(item_count)); + + for (DWORD item_index = 0U; item_index < item_count; ++item_index) + { + IShellItem *item; + HRESULT res_shell_item_array_get_item_at = item_array->GetItemAt(item_index, &item); + assert(SUCCEEDED(res_shell_item_array_get_item_at)); + + WCHAR *name; + HRESULT res_shell_item_get_display_name = item->GetDisplayName(SIGDN_FILESYSPATH, &name); + assert(SUCCEEDED(res_shell_item_get_display_name)); + + std::wstring file_name(name); + + CoTaskMemFree(name); + + item->Release(); + + bool res_convert_utf16_To_utf8_string = llvm::convertUTF16ToUTF8String(file_name, file_names[item_index]); + assert(res_convert_utf16_To_utf8_string); + } + + item_array->Release(); + } + else + { + assert(HRESULT_FROM_WIN32(ERROR_CANCELLED) == res_file_open_dialog_show); + return 0; + } + + file_open_dialog->Release(); + + CoUninitialize(); + } + else + { + file_names.resize(static_cast(argc - 1)); + + for (int arg_index = 1; arg_index < argc; ++arg_index) + { + std::wstring file_name(argv[1]); + bool res_convert_utf16_To_utf8_string = llvm::convertUTF16ToUTF8String(file_name, file_names[arg_index - 1]); + assert(res_convert_utf16_To_utf8_string); + } + } + } + + HINSTANCE hInstance = reinterpret_cast(&__ImageBase); + + g_window_width = 1280; + g_window_height = 720; + + ATOM hWndCls; + { + WNDCLASSEXW Desc = { + sizeof(WNDCLASSEX), + CS_OWNDC, + wnd_proc, + 0, + 0, + hInstance, + LoadIconW(NULL, IDI_APPLICATION), + LoadCursorW(NULL, IDC_ARROW), + (HBRUSH)(COLOR_WINDOW + 1), + NULL, + L"glTF-Viewer:0XFFFFFFFF", + LoadIconW(NULL, IDI_APPLICATION), + }; + hWndCls = RegisterClassExW(&Desc); + } + + HWND hWnd; + { + HWND hDesktop = GetDesktopWindow(); + HMONITOR hMonitor = MonitorFromWindow(hDesktop, MONITOR_DEFAULTTONEAREST); + MONITORINFOEXW MonitorInfo; + MonitorInfo.cbSize = sizeof(MONITORINFOEXW); + GetMonitorInfoW(hMonitor, &MonitorInfo); + + constexpr DWORD const dw_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; + constexpr DWORD const dw_ex_style = WS_EX_APPWINDOW; + + RECT rect = {(MonitorInfo.rcWork.left + MonitorInfo.rcWork.right) / 2 - g_window_width / 2, + (MonitorInfo.rcWork.bottom + MonitorInfo.rcWork.top) / 2 - g_window_height / 2, + (MonitorInfo.rcWork.left + MonitorInfo.rcWork.right) / 2 + g_window_width / 2, + (MonitorInfo.rcWork.bottom + MonitorInfo.rcWork.top) / 2 + g_window_height / 2}; + AdjustWindowRectEx(&rect, dw_style, FALSE, dw_ex_style); + + hWnd = CreateWindowExW(dw_ex_style, MAKEINTATOM(hWndCls), L"glTF Viewer", dw_style, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hDesktop, NULL, hInstance, NULL); + } + + g_renderer = renderer_init(NULL, file_names); + + renderer_attach_window(g_renderer, hWnd); + + ShowWindow(hWnd, SW_SHOWDEFAULT); + + BOOL result_update_window = UpdateWindow(hWnd); + assert(FALSE != result_update_window); + } + + // Run + { + MSG msg; + while (GetMessageW(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + // Destroy + { + renderer_dettach_window(g_renderer); + + renderer_destroy(g_renderer); + } + + return 0; +} + +static LRESULT CALLBACK wnd_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_DESTROY: + { + PostQuitMessage(0); + } + return 0; + case WM_PAINT: + { + renderer_draw(g_renderer); + } + return 0; + case WM_SIZE: + { + WORD const new_width = LOWORD(lParam); + WORD const new_height = HIWORD(lParam); + + if (g_window_width != new_width || g_window_height != new_height) + { + renderer_on_window_resize(g_renderer); + g_window_width = new_width; + g_window_height = new_height; + } + } + return 0; + case WM_ERASEBKGND: + return 1; + case WM_KEYDOWN: + { + switch (wParam) + { + case 'W': + { + g_camera_controller.MoveForward(); + } + break; + case 'S': + { + g_camera_controller.MoveBack(); + } + break; + case 'A': + { + g_camera_controller.MoveLeft(); + } + break; + case 'D': + { + g_camera_controller.MoveRight(); + } + break; + case 'Q': + { + g_camera_controller.MoveDown(); + } + break; + case 'E': + { + g_camera_controller.MoveUp(); + } + break; + } + } + return 0; + case WM_MOUSEMOVE: + { + int Current_X = GET_X_LPARAM(lParam); + int Current_Y = GET_Y_LPARAM(lParam); + + float CurrentNormalized_X = static_cast(Current_X) / g_window_width; + float CurrentNormalized_Y = static_cast(Current_Y) / g_window_height; + + g_camera_controller.OnMouseMove(CurrentNormalized_X, CurrentNormalized_Y, (0 != (wParam & MK_RBUTTON))); + } + return 0; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } +} + +static HRESULT STDMETHODCALLTYPE NoRegCoCreate(const __wchar_t *dllName, REFCLSID rclsid, REFIID riid, void **ppv) +{ + HMODULE dynamic_library = LoadLibraryW(dllName); + if (NULL == dynamic_library) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + decltype(DllGetClassObject) *pfn_dll_get_class_object = reinterpret_cast(GetProcAddress(dynamic_library, "DllGetClassObject")); + + if (NULL == pfn_dll_get_class_object) + { + FreeLibrary(dynamic_library); + return HRESULT_FROM_WIN32(GetLastError()); + } + + IClassFactory *class_factory = nullptr; + HRESULT hr_dll_get_class_object = pfn_dll_get_class_object(rclsid, IID_PPV_ARGS(&class_factory)); + if (!SUCCEEDED(hr_dll_get_class_object)) + { + FreeLibrary(dynamic_library); + return hr_dll_get_class_object; + } + + HRESULT hr_class_factory_create_instance = class_factory->CreateInstance(nullptr, riid, ppv); + class_factory->Release(); + + // TODO: DllCanUnloadNow + + return hr_class_factory_create_instance; +} + +#else +#error Unknown Compiler +#endif \ No newline at end of file diff --git a/source/support/renderer.cpp b/source/support/renderer.cpp new file mode 100644 index 0000000..040ffaa --- /dev/null +++ b/source/support/renderer.cpp @@ -0,0 +1,435 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include +#include +#include +#include + +#include "renderer.h" +#include "frame_throttling.h" +#include "tick_count.h" +#include "../../thirdparty/Brioche/include/brx_device.h" +#include "../demo.h" + +class renderer +{ + brx_device *m_device; + + brx_graphics_queue *m_graphics_queue; + + uint32_t m_frame_throttling_index; + + brx_graphics_command_buffer *m_command_buffers[FRAME_THROTTLING_COUNT]; + brx_fence *m_fences[FRAME_THROTTLING_COUNT]; + + brx_surface *m_surface; + brx_swap_chain *m_swap_chain; + + Demo m_demo; + + brx_pipeline_layout *m_full_screen_transfer_pipeline_layout; + + brx_render_pass *m_full_screen_transfer_render_pass; + brx_graphics_pipeline *m_full_screen_transfer_pipeline; + + brx_descriptor_set_layout *m_full_screen_transfer_pipeline_none_update_descriptor_set_layout; + brx_descriptor_set *m_full_screen_transfer_pipeline_none_update_descriptor_set; + + BRX_COLOR_ATTACHMENT_IMAGE_FORMAT m_swap_chain_image_format; + uint32_t m_swap_chain_image_width; + uint32_t m_swap_chain_image_height; + std::vector m_swap_chain_frame_buffers; + + double m_tick_count_resolution; + uint64_t m_tick_count_previous_frame; + + void attach_swap_chain(); + + void dettach_swap_chain(); + + void create_swap_chain_render_pass_and_pipeline(); + + void destroy_swap_chain_render_pass_and_pipeline(); + +public: + renderer(); + + ~renderer(); + + void init(void *wsi_connection, std::vector const &file_names); + + void destroy(); + + void attach_window(void *wsi_window); + + void on_window_resize(); + + void dettach_window(); + + void draw(); +}; + +renderer::renderer() : m_surface(NULL), m_swap_chain(NULL) +{ +} + +renderer::~renderer() +{ + for (uint32_t frame_throtting_index = 0U; frame_throtting_index < FRAME_THROTTLING_COUNT; ++frame_throtting_index) + { + assert(NULL == this->m_command_buffers[frame_throtting_index]); + assert(NULL == this->m_fences[frame_throtting_index]); + } + + assert(NULL == this->m_surface); + assert(NULL == this->m_swap_chain); + + assert(NULL == this->m_device); +} + +extern renderer *renderer_init(void *wsi_connection, std::vector const &file_names) +{ + renderer *new_renderer = new (malloc(sizeof(renderer))) renderer{}; + new_renderer->init(wsi_connection, file_names); + return new_renderer; +} + +extern void renderer_destroy(renderer *renderer) +{ + renderer->destroy(); + renderer->~renderer(); + free(renderer); +} + +extern void renderer_attach_window(renderer *renderer, void *wsi_window) +{ + renderer->attach_window(wsi_window); +} + +extern void renderer_on_window_resize(renderer *renderer) +{ + renderer->on_window_resize(); +} + +extern void renderer_dettach_window(renderer *renderer) +{ + renderer->dettach_window(); +} + +extern void renderer_draw(renderer *renderer) +{ + renderer->draw(); +} + +void renderer::init(void *wsi_connection, std::vector const &file_names) +{ + this->m_device = brx_init_unknown_device(wsi_connection, false); + + this->m_graphics_queue = this->m_device->create_graphics_queue(); + + this->m_frame_throttling_index = 0U; + + for (uint32_t frame_throtting_index = 0U; frame_throtting_index < FRAME_THROTTLING_COUNT; ++frame_throtting_index) + { + this->m_command_buffers[frame_throtting_index] = this->m_device->create_graphics_command_buffer(); + this->m_fences[frame_throtting_index] = this->m_device->create_fence(true); + } + + // Demo Init + this->m_demo.init(this->m_device, file_names); + + // Descriptor Layout + { + BRX_DESCRIPTOR_SET_LAYOUT_BINDING full_screen_transfer_none_update_descriptor_set_layout_bindings[] = { + // texture and sampler + {0U, BRX_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1U}}; + this->m_full_screen_transfer_pipeline_none_update_descriptor_set_layout = this->m_device->create_descriptor_set_layout(sizeof(full_screen_transfer_none_update_descriptor_set_layout_bindings) / sizeof(full_screen_transfer_none_update_descriptor_set_layout_bindings[0]), full_screen_transfer_none_update_descriptor_set_layout_bindings); + + brx_descriptor_set_layout *const full_screen_transfer_descriptor_set_layouts[] = {this->m_full_screen_transfer_pipeline_none_update_descriptor_set_layout}; + this->m_full_screen_transfer_pipeline_layout = this->m_device->create_pipeline_layout(sizeof(full_screen_transfer_descriptor_set_layouts) / sizeof(full_screen_transfer_descriptor_set_layouts[0]), full_screen_transfer_descriptor_set_layouts); + } + + // Pipeline + { + this->m_swap_chain_image_format = BRX_COLOR_ATTACHMENT_FORMAT_B8G8R8A8_UNORM; + this->m_full_screen_transfer_render_pass = NULL; + this->m_full_screen_transfer_pipeline = NULL; + this->create_swap_chain_render_pass_and_pipeline(); + } + + // Descriptor + { + this->m_full_screen_transfer_pipeline_none_update_descriptor_set = this->m_device->create_descriptor_set(this->m_full_screen_transfer_pipeline_none_update_descriptor_set_layout); + } + + // Init SwapChain Related + this->m_swap_chain_image_width = 0U; + this->m_swap_chain_image_height = 0U; + assert(0U == this->m_swap_chain_frame_buffers.size()); + + // Tick Count + this->m_tick_count_resolution = (1.0 / static_cast(tick_count_per_second())); + this->m_tick_count_previous_frame = tick_count_now(); +} + +void renderer::destroy() +{ + for (uint32_t frame_throtting_index = 0U; frame_throtting_index < FRAME_THROTTLING_COUNT; ++frame_throtting_index) + { + this->m_device->wait_for_fence(this->m_fences[frame_throtting_index]); + } + + this->m_demo.destroy(this->m_device); + + this->m_device->destroy_descriptor_set(this->m_full_screen_transfer_pipeline_none_update_descriptor_set); + + this->m_device->destroy_descriptor_set_layout(this->m_full_screen_transfer_pipeline_none_update_descriptor_set_layout); + + this->destroy_swap_chain_render_pass_and_pipeline(); + + this->m_device->destroy_pipeline_layout(this->m_full_screen_transfer_pipeline_layout); + + for (uint32_t frame_throtting_index = 0U; frame_throtting_index < FRAME_THROTTLING_COUNT; ++frame_throtting_index) + { + this->m_device->destroy_graphics_command_buffer(this->m_command_buffers[frame_throtting_index]); + this->m_command_buffers[frame_throtting_index] = NULL; + this->m_device->destroy_fence(this->m_fences[frame_throtting_index]); + this->m_fences[frame_throtting_index] = NULL; + } + + brx_destroy_unknown_device(this->m_device); + this->m_device = NULL; +} + +void renderer::attach_window(void *wsi_window) +{ + assert(NULL == this->m_surface); + + this->m_surface = this->m_device->create_surface(wsi_window); + this->attach_swap_chain(); +} + +void renderer::on_window_resize() +{ + for (uint32_t frame_throtting_index = 0U; frame_throtting_index < FRAME_THROTTLING_COUNT; ++frame_throtting_index) + { + this->m_device->wait_for_fence(this->m_fences[frame_throtting_index]); + } + + this->dettach_swap_chain(); + this->attach_swap_chain(); +} + +void renderer::dettach_window() +{ + for (uint32_t frame_throtting_index = 0U; frame_throtting_index < FRAME_THROTTLING_COUNT; ++frame_throtting_index) + { + this->m_device->wait_for_fence(this->m_fences[frame_throtting_index]); + } + + this->dettach_swap_chain(); + this->m_device->destroy_surface(this->m_surface); + + this->m_surface = NULL; + this->m_swap_chain = NULL; +} + +void renderer::attach_swap_chain() +{ + assert(NULL == this->m_swap_chain); + this->m_swap_chain = this->m_device->create_swap_chain(this->m_surface); + + assert(0U == this->m_swap_chain_image_width); + assert(0U == this->m_swap_chain_image_height); + this->m_swap_chain_image_width = this->m_swap_chain->get_image_width(); + this->m_swap_chain_image_height = this->m_swap_chain->get_image_height(); + + if (this->m_swap_chain_image_format != this->m_swap_chain->get_image_format()) + { + this->m_swap_chain_image_format = this->m_swap_chain->get_image_format(); + this->destroy_swap_chain_render_pass_and_pipeline(); + this->create_swap_chain_render_pass_and_pipeline(); + } + + uint32_t const swap_chain_image_count = this->m_swap_chain->get_image_count(); + assert(0U == this->m_swap_chain_frame_buffers.size()); + this->m_swap_chain_frame_buffers.resize(swap_chain_image_count); + + for (uint32_t swap_chain_image_index = 0U; swap_chain_image_index < swap_chain_image_count; ++swap_chain_image_index) + { + brx_color_attachment_image const *const swap_chain_color_attachment_image = this->m_swap_chain->get_image(swap_chain_image_index); + + this->m_swap_chain_frame_buffers[swap_chain_image_index] = this->m_device->create_frame_buffer(this->m_full_screen_transfer_render_pass, this->m_swap_chain_image_width, this->m_swap_chain_image_height, 1U, &swap_chain_color_attachment_image, NULL); + } + + this->m_demo.on_swap_chain_attach(this->m_device, this->m_swap_chain_image_width, this->m_swap_chain_image_height); + + // Descriptor + { + // Full Screen Transfer Pipeline + { + // The VkDescriptorSetLayout should still be valid when perform write update on VkDescriptorSet + assert(NULL != this->m_full_screen_transfer_pipeline_none_update_descriptor_set_layout); + { + brx_sampled_image const *const sampled_images[] = { + this->m_demo.get_sampled_image_for_present()}; + this->m_device->write_descriptor_set(this->m_full_screen_transfer_pipeline_none_update_descriptor_set, 0U, BRX_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0U, sizeof(sampled_images) / sizeof(sampled_images[0]), NULL, NULL, NULL, NULL, sampled_images, NULL, NULL, NULL); + } + } + } +} + +void renderer::dettach_swap_chain() +{ + this->m_demo.on_swap_chain_dettach(this->m_device); + + uint32_t const swap_chain_image_count = static_cast(this->m_swap_chain_frame_buffers.size()); + assert(this->m_swap_chain->get_image_count() == swap_chain_image_count); + + for (uint32_t swap_chain_image_index = 0U; swap_chain_image_index < swap_chain_image_count; ++swap_chain_image_index) + { + this->m_device->destroy_frame_buffer(this->m_swap_chain_frame_buffers[swap_chain_image_index]); + } + + this->m_swap_chain_frame_buffers.clear(); + + this->m_swap_chain_image_width = 0U; + this->m_swap_chain_image_height = 0U; + + this->m_device->destroy_swap_chain(this->m_swap_chain); + this->m_swap_chain = NULL; +} + +void renderer::create_swap_chain_render_pass_and_pipeline() +{ + assert(NULL == this->m_full_screen_transfer_render_pass); + assert(NULL == this->m_full_screen_transfer_pipeline); + + // Render Pass + { + BRX_RENDER_PASS_COLOR_ATTACHMENT color_attachments[1] = { + {this->m_swap_chain_image_format, + BRX_RENDER_PASS_COLOR_ATTACHMENT_LOAD_OPERATION_CLEAR, + BRX_RENDER_PASS_COLOR_ATTACHMENT_STORE_OPERATION_FLUSH_FOR_PRESENT}}; + + this->m_full_screen_transfer_render_pass = this->m_device->create_render_pass(sizeof(color_attachments) / sizeof(color_attachments[0]), color_attachments, NULL); + } + + // Pipeline + { +#include +#include + this->m_full_screen_transfer_pipeline = this->m_device->create_graphics_pipeline(this->m_full_screen_transfer_render_pass, this->m_full_screen_transfer_pipeline_layout, sizeof(full_screen_transfer_vertex_shader_module_code), full_screen_transfer_vertex_shader_module_code, sizeof(full_screen_transfer_fragment_shader_module_code), full_screen_transfer_fragment_shader_module_code, false, BRX_GRAPHICS_PIPELINE_COMPARE_OPERATION_ALWAYS); + } +} + +void renderer::destroy_swap_chain_render_pass_and_pipeline() +{ + this->m_device->destroy_render_pass(this->m_full_screen_transfer_render_pass); + this->m_device->destroy_graphics_pipeline(this->m_full_screen_transfer_pipeline); + + this->m_full_screen_transfer_render_pass = NULL; + this->m_full_screen_transfer_pipeline = NULL; +} + +void renderer::draw() +{ + if (NULL == this->m_surface) + { + // skip this frame + return; + } + + assert(NULL != this->m_swap_chain); + + this->m_device->wait_for_fence(this->m_fences[this->m_frame_throttling_index]); + + this->m_device->reset_graphics_command_buffer(this->m_command_buffers[this->m_frame_throttling_index]); + + this->m_command_buffers[this->m_frame_throttling_index]->begin(); + + { + uint64_t const tick_count_current_frame = tick_count_now(); + float const interval_time = static_cast(static_cast(tick_count_current_frame - this->m_tick_count_previous_frame) * this->m_tick_count_resolution); + this->m_tick_count_previous_frame = tick_count_current_frame; + + this->m_demo.draw(this->m_command_buffers[this->m_frame_throttling_index], interval_time, this->m_frame_throttling_index); + } + + uint32_t swap_chain_image_index = -1; + bool acquire_next_image_not_out_of_date = this->m_device->acquire_next_image(this->m_command_buffers[this->m_frame_throttling_index], this->m_swap_chain, &swap_chain_image_index); + if (!acquire_next_image_not_out_of_date) + { + // NOTE: we should end the command buffer before we destroy the bound image + this->m_command_buffers[this->m_frame_throttling_index]->end(); + + for (uint32_t frame_throtting_index = 0U; frame_throtting_index < FRAME_THROTTLING_COUNT; ++frame_throtting_index) + { + this->m_device->wait_for_fence(this->m_fences[frame_throtting_index]); + } + + this->dettach_swap_chain(); + this->attach_swap_chain(); + + // skip this frame + return; + } + + // draw full screen triangle + { + this->m_command_buffers[this->m_frame_throttling_index]->begin_debug_utils_label("Full Screen Transfer Pass"); + + float color_clear_values[4] = {0.0F, 0.0F, 0.0F, 0.0F}; + this->m_command_buffers[this->m_frame_throttling_index]->begin_render_pass(this->m_full_screen_transfer_render_pass, this->m_swap_chain_frame_buffers[swap_chain_image_index], this->m_swap_chain_image_width, this->m_swap_chain_image_height, 1U, &color_clear_values, NULL, NULL); + + this->m_command_buffers[this->m_frame_throttling_index]->bind_graphics_pipeline(this->m_full_screen_transfer_pipeline); + + this->m_command_buffers[this->m_frame_throttling_index]->set_view_port(this->m_swap_chain_image_width, this->m_swap_chain_image_height); + + this->m_command_buffers[this->m_frame_throttling_index]->set_scissor(this->m_swap_chain_image_width, this->m_swap_chain_image_height); + + brx_descriptor_set *const descritor_sets[1] = {this->m_full_screen_transfer_pipeline_none_update_descriptor_set}; + this->m_command_buffers[this->m_frame_throttling_index]->bind_graphics_descriptor_sets(this->m_full_screen_transfer_pipeline_layout, sizeof(descritor_sets) / sizeof(descritor_sets[0]), descritor_sets, 0U, NULL); + this->m_command_buffers[this->m_frame_throttling_index]->draw(3U, 1U); + + this->m_command_buffers[this->m_frame_throttling_index]->end_render_pass(); + + this->m_command_buffers[this->m_frame_throttling_index]->end_debug_utils_label(); + } + + this->m_command_buffers[this->m_frame_throttling_index]->end(); + + this->m_device->reset_fence(this->m_fences[this->m_frame_throttling_index]); + + bool present_not_out_of_date = this->m_graphics_queue->submit_and_present(this->m_command_buffers[this->m_frame_throttling_index], this->m_swap_chain, swap_chain_image_index, this->m_fences[this->m_frame_throttling_index]); + if (!present_not_out_of_date) + { + for (uint32_t frame_throtting_index = 0U; frame_throtting_index < FRAME_THROTTLING_COUNT; ++frame_throtting_index) + { + this->m_device->wait_for_fence(this->m_fences[frame_throtting_index]); + } + + this->dettach_swap_chain(); + this->attach_swap_chain(); + + // continue this frame + } + + ++this->m_frame_throttling_index; + this->m_frame_throttling_index %= FRAME_THROTTLING_COUNT; +} diff --git a/source/support/renderer.h b/source/support/renderer.h new file mode 100644 index 0000000..d01a053 --- /dev/null +++ b/source/support/renderer.h @@ -0,0 +1,31 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _RENDERER_H_ +#define _RENDERER_H_ 1 + +#include +#include + +extern class renderer *renderer_init(void *wsi_connection, std::vector const &file_names); +extern void renderer_destroy(class renderer *renderer); +extern void renderer_attach_window(class renderer *renderer, void *wsi_window); +extern void renderer_on_window_resize(class renderer *renderer); +extern void renderer_dettach_window(class renderer *renderer); +extern void renderer_draw(class renderer *renderer); + +#endif \ No newline at end of file diff --git a/source/support/tick_count.cpp b/source/support/tick_count.cpp new file mode 100644 index 0000000..08f2f3e --- /dev/null +++ b/source/support/tick_count.cpp @@ -0,0 +1,70 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#include "tick_count.h" +#include + +#if defined(__GNUC__) + +#include + +extern uint64_t tick_count_per_second() +{ + constexpr uint64_t const tick_count_per_second = 1000000000ULL; + return tick_count_per_second; +} + +extern uint64_t tick_count_now() +{ + struct timespec time_monotonic; + int result_clock_get_time_monotonic = clock_gettime(CLOCK_MONOTONIC, &time_monotonic); + assert(0 == result_clock_get_time_monotonic); + + uint64_t const tick_count_now = static_cast(1000000000ULL) * static_cast(time_monotonic.tv_sec) + static_cast(time_monotonic.tv_nsec); + return tick_count_now; +} + +#elif defined(_MSC_VER) + +#define NOMINMAX 1 +#define WIN32_LEAN_AND_MEAN 1 +#include +#include + +extern uint64_t tick_count_per_second() +{ + LARGE_INTEGER int64_frequency; + BOOL result_query_performance_frequency = QueryPerformanceFrequency(&int64_frequency); + assert(NULL != result_query_performance_frequency); + + uint64_t const tick_count_per_second = static_cast(int64_frequency.QuadPart); + return tick_count_per_second; +} + +extern uint64_t tick_count_now() +{ + LARGE_INTEGER int64_performance_count; + BOOL result_query_performance_counter = QueryPerformanceCounter(&int64_performance_count); + assert(NULL != result_query_performance_counter); + + uint64_t const tick_count_now = static_cast(int64_performance_count.QuadPart); + return tick_count_now; +} + +#else +#error Unknown Compiler +#endif \ No newline at end of file diff --git a/source/support/tick_count.h b/source/support/tick_count.h new file mode 100644 index 0000000..db67e45 --- /dev/null +++ b/source/support/tick_count.h @@ -0,0 +1,28 @@ +// +// Copyright (C) YuqiaoZhang(HanetakaChou) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// + +#ifndef _TICK_COUNT_H_ +#define _TICK_COUNT_H_ 1 + +#include +#include + +extern uint64_t tick_count_per_second(); + +extern uint64_t tick_count_now(); + +#endif diff --git a/spirv/.gitignore b/spirv/.gitignore new file mode 100644 index 0000000..70a618f --- /dev/null +++ b/spirv/.gitignore @@ -0,0 +1,2 @@ +/debug +/release diff --git a/spirv/deferred_shading_fragment.inl b/spirv/deferred_shading_fragment.inl new file mode 100644 index 0000000..6263fb9 --- /dev/null +++ b/spirv/deferred_shading_fragment.inl @@ -0,0 +1,7 @@ +constexpr uint32_t const deferred_shading_fragment_shader_module_code[] = { +#ifndef NDEBUG +#include "debug/_internal_deferred_shading_fragment.inl" +#else +#include "release/_internal_deferred_shading_fragment.inl" +#endif +}; \ No newline at end of file diff --git a/spirv/deferred_shading_vertex.inl b/spirv/deferred_shading_vertex.inl new file mode 100644 index 0000000..c8e2759 --- /dev/null +++ b/spirv/deferred_shading_vertex.inl @@ -0,0 +1,7 @@ +constexpr uint32_t const deferred_shading_vertex_shader_module_code[] = { +#ifndef NDEBUG +#include "debug/_internal_deferred_shading_vertex.inl" +#else +#include "release/_internal_deferred_shading_vertex.inl" +#endif +}; \ No newline at end of file diff --git a/spirv/full_screen_transfer_fragment.inl b/spirv/full_screen_transfer_fragment.inl new file mode 100644 index 0000000..02af46e --- /dev/null +++ b/spirv/full_screen_transfer_fragment.inl @@ -0,0 +1,7 @@ +constexpr uint32_t const full_screen_transfer_fragment_shader_module_code[] = { +#ifndef NDEBUG +#include "debug/_internal_full_screen_transfer_fragment.inl" +#else +#include "release/_internal_full_screen_transfer_fragment.inl" +#endif +}; diff --git a/spirv/full_screen_transfer_vertex.inl b/spirv/full_screen_transfer_vertex.inl new file mode 100644 index 0000000..739c407 --- /dev/null +++ b/spirv/full_screen_transfer_vertex.inl @@ -0,0 +1,7 @@ +constexpr uint32_t const full_screen_transfer_vertex_shader_module_code[] = { +#ifndef NDEBUG +#include "debug/_internal_full_screen_transfer_vertex.inl" +#else +#include "release/_internal_full_screen_transfer_vertex.inl" +#endif +}; diff --git a/spirv/gbuffer_fragment.inl b/spirv/gbuffer_fragment.inl new file mode 100644 index 0000000..9fbf8c7 --- /dev/null +++ b/spirv/gbuffer_fragment.inl @@ -0,0 +1,7 @@ +constexpr uint32_t const gbuffer_fragment_shader_module_code[] = { +#ifndef NDEBUG +#include "debug/_internal_gbuffer_fragment.inl" +#else +#include "release/_internal_gbuffer_fragment.inl" +#endif +}; \ No newline at end of file diff --git a/spirv/gbuffer_vertex.inl b/spirv/gbuffer_vertex.inl new file mode 100644 index 0000000..6136f97 --- /dev/null +++ b/spirv/gbuffer_vertex.inl @@ -0,0 +1,7 @@ +constexpr uint32_t const gbuffer_vertex_shader_module_code[] = { +#ifndef NDEBUG +#include "debug/_internal_gbuffer_vertex.inl" +#else +#include "release/_internal_gbuffer_vertex.inl" +#endif +}; \ No newline at end of file diff --git a/spirv/skin_compute.inl b/spirv/skin_compute.inl new file mode 100644 index 0000000..295eec3 --- /dev/null +++ b/spirv/skin_compute.inl @@ -0,0 +1,7 @@ +constexpr uint32_t const skin_compute_shader_module_code[] = { +#ifndef NDEBUG +#include "debug/_internal_skin_compute.inl" +#else +#include "release/_internal_skin_compute.inl" +#endif +}; \ No newline at end of file diff --git a/thirdparty/Brioche b/thirdparty/Brioche new file mode 160000 index 0000000..18ede08 --- /dev/null +++ b/thirdparty/Brioche @@ -0,0 +1 @@ +Subproject commit 18ede08be67d0a6fc2997926e471ab5018b069c0 diff --git a/thirdparty/ConvertUTF b/thirdparty/ConvertUTF new file mode 160000 index 0000000..5b93321 --- /dev/null +++ b/thirdparty/ConvertUTF @@ -0,0 +1 @@ +Subproject commit 5b93321790018086ff99eb9d51acc0d0a95215eb diff --git a/thirdparty/CoreRT b/thirdparty/CoreRT new file mode 160000 index 0000000..a4d26a9 --- /dev/null +++ b/thirdparty/CoreRT @@ -0,0 +1 @@ +Subproject commit a4d26a90cba87a966769e154fcb01464800bd9ad diff --git a/thirdparty/DLB b/thirdparty/DLB new file mode 160000 index 0000000..19e0911 --- /dev/null +++ b/thirdparty/DLB @@ -0,0 +1 @@ +Subproject commit 19e09110ff8024afcc8d431fd462656501c9cb59 diff --git a/thirdparty/DirectXMath b/thirdparty/DirectXMath new file mode 160000 index 0000000..0ab21b3 --- /dev/null +++ b/thirdparty/DirectXMath @@ -0,0 +1 @@ +Subproject commit 0ab21b3808cba7c009d7a086b000469ba0e83f73 diff --git a/thirdparty/ImportAsset b/thirdparty/ImportAsset new file mode 160000 index 0000000..e8d0309 --- /dev/null +++ b/thirdparty/ImportAsset @@ -0,0 +1 @@ +Subproject commit e8d0309088fa7f6942cc679d101842b45aee415c