├── .clang-tidy ├── .gitattributes ├── .github └── workflows │ ├── cmake_linux.yml │ ├── cmake_macos.yml │ └── cmake_windows.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── OPCODE_CORE.md ├── OPCODE_EXT.md ├── README.md ├── example ├── CMakeLists.txt ├── main.cpp └── shaders │ ├── simple.frag │ ├── simple.frag.spv │ └── simple.frag.spv.txt ├── images ├── Nt3BzM.png ├── XdlSDs.png ├── fstyD4.png └── ftdfWn.png ├── shadertoy ├── CMakeLists.txt ├── assets │ ├── images │ │ ├── awesomeface.png │ │ └── wood.png │ ├── shaders │ │ ├── Nt3BzM.frag │ │ ├── XdlSDs.frag │ │ ├── basic.frag │ │ ├── fstyD4.frag │ │ └── ftdfWn.frag │ └── wrapper.frag ├── compiler.cpp ├── compiler.h ├── main.cpp ├── renderer.cpp ├── renderer.h ├── resourcelimits.cpp ├── resourcelimits.h ├── settingpanel.cpp ├── settingpanel.h ├── settings.h ├── third_party │ ├── glad │ │ ├── include │ │ │ ├── KHR │ │ │ │ └── khrplatform.h │ │ │ └── glad │ │ │ │ └── glad.h │ │ └── src │ │ │ └── glad.c │ ├── glfw │ │ ├── include │ │ │ └── GLFW │ │ │ │ ├── glfw3.h │ │ │ │ └── glfw3native.h │ │ ├── lib-macos-universal │ │ │ ├── libglfw.3.dylib │ │ │ └── libglfw3.a │ │ ├── lib-mingw-w64 │ │ │ ├── glfw3.dll │ │ │ ├── libglfw3.a │ │ │ └── libglfw3dll.a │ │ └── lib-vc2022 │ │ │ ├── glfw3.dll │ │ │ ├── glfw3.lib │ │ │ ├── glfw3_mt.lib │ │ │ └── glfw3dll.lib │ ├── imgui │ │ └── imgui │ │ │ ├── imconfig.h │ │ │ ├── imgui.cpp │ │ │ ├── imgui.h │ │ │ ├── imgui_demo.cpp │ │ │ ├── imgui_draw.cpp │ │ │ ├── imgui_impl_glfw.cpp │ │ │ ├── imgui_impl_glfw.h │ │ │ ├── imgui_impl_opengl3.cpp │ │ │ ├── imgui_impl_opengl3.h │ │ │ ├── imgui_impl_opengl3_loader.h │ │ │ ├── imgui_internal.h │ │ │ ├── imgui_tables.cpp │ │ │ ├── imgui_widgets.cpp │ │ │ ├── imstb_rectpack.h │ │ │ ├── imstb_textedit.h │ │ │ └── imstb_truetype.h │ └── stb │ │ └── include │ │ └── stb │ │ ├── stb_image.h │ │ └── stb_image_write.h ├── threadpool.h ├── timer.cpp └── timer.h ├── src ├── CMakeLists.txt ├── decoder.cpp ├── decoder.h ├── ext │ ├── GLSL.std.450.h │ └── GLSL.std.450.inc ├── image.cpp ├── image.h ├── interface.cpp ├── interface.h ├── logger.cpp ├── logger.h ├── module.cpp ├── module.h ├── opcodes.inc ├── opstrings.h ├── runtime.cpp ├── runtime.h ├── spirv.h ├── spvm.cpp ├── spvm.h └── utils.h └── test ├── CMakeLists.txt ├── assets ├── arithmetic_0.frag ├── arithmetic_0.frag.spv ├── arithmetic_0.frag.spv.txt ├── arithmetic_1.frag ├── arithmetic_1.frag.spv ├── arithmetic_1.frag.spv.txt ├── array.frag ├── array.frag.spv ├── array.frag.spv.txt ├── assert.glsl ├── bit.frag ├── bit.frag.spv ├── bit.frag.spv.txt ├── built_in.vert ├── built_in.vert.spv ├── built_in.vert.spv.txt ├── composite.frag ├── composite.frag.spv ├── composite.frag.spv.txt ├── control_flow.frag ├── control_flow.frag.spv ├── control_flow.frag.spv.txt ├── conversion.frag ├── conversion.frag.spv ├── conversion.frag.spv.txt ├── derivative.frag ├── derivative.frag.spv ├── derivative.frag.spv.txt ├── function.frag ├── function.frag.spv ├── function.frag.spv.txt ├── glsl_std_450_0.frag ├── glsl_std_450_0.frag.spv ├── glsl_std_450_0.frag.spv.txt ├── glsl_std_450_1.frag ├── glsl_std_450_1.frag.spv ├── glsl_std_450_1.frag.spv.txt ├── glsl_std_450_2.frag ├── glsl_std_450_2.frag.spv ├── glsl_std_450_2.frag.spv.txt ├── image.frag ├── image.frag.spv ├── image.frag.spv.txt ├── location.frag ├── location.frag.spv ├── location.frag.spv.txt ├── relational_logical.frag ├── relational_logical.frag.spv ├── relational_logical.frag.spv.txt ├── uniform_block.vert ├── uniform_block.vert.spv └── uniform_block.vert.spv.txt ├── main.cpp ├── test.h ├── test_core.cpp ├── test_ext.cpp ├── test_image.cpp └── tools └── glsl.py /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: '-modernize-use-auto' -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | shadertoy/third_party/** linguist-vendored 2 | test/googletest/** linguist-vendored -------------------------------------------------------------------------------- /.github/workflows/cmake_linux.yml: -------------------------------------------------------------------------------- 1 | name: CMake Linux 2 | 3 | on: 4 | push: 5 | branches: [ main, dev ] 6 | pull_request: 7 | branches: [ main, dev ] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | 12 | jobs: 13 | build_linux: 14 | name: build_linux 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | submodules: true 21 | 22 | - name: Install OpenGL Library 23 | run: sudo apt-get install -y libglfw3-dev libgl1-mesa-dev libglu1-mesa-dev libglfw3 libglfw3-dev 24 | 25 | - name: Configure CMake 26 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 27 | 28 | - name: Build 29 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 30 | 31 | - name: Test 32 | run: cd ${{github.workspace}}/build && ctest 33 | -------------------------------------------------------------------------------- /.github/workflows/cmake_macos.yml: -------------------------------------------------------------------------------- 1 | name: CMake MacOS 2 | 3 | on: 4 | push: 5 | branches: [ main, dev ] 6 | pull_request: 7 | branches: [ main, dev ] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | 12 | jobs: 13 | build_macOS: 14 | name: build_macOS 15 | runs-on: macos-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | submodules: true 21 | 22 | - name: Configure CMake 23 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 24 | 25 | - name: Build 26 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 27 | 28 | - name: Test 29 | run: cd ${{github.workspace}}/build && ctest 30 | -------------------------------------------------------------------------------- /.github/workflows/cmake_windows.yml: -------------------------------------------------------------------------------- 1 | name: CMake Windows 2 | 3 | on: 4 | push: 5 | branches: [ main, dev ] 6 | pull_request: 7 | branches: [ main, dev ] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | 12 | jobs: 13 | build_windows: 14 | name: build_windows 15 | runs-on: windows-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | submodules: true 21 | 22 | - name: Configure CMake 23 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 24 | 25 | - name: Build 26 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 27 | 28 | - name: Test 29 | run: cd ${{github.workspace}}/build && ctest 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | cmake-build*/ 4 | build/ 5 | bin/spvm* 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "shadertoy/third_party/glslang"] 2 | path = shadertoy/third_party/glslang 3 | url = https://github.com/KhronosGroup/glslang 4 | [submodule "test/googletest"] 5 | path = test/googletest 6 | url = https://github.com/google/googletest 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3) 2 | project(spvm) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | add_subdirectory(src) 7 | add_subdirectory(shadertoy) 8 | 9 | option(BUILD_EXAMPLE "Whether or not to build the example" ON) 10 | if (${BUILD_EXAMPLE}) 11 | message(STATUS "Building example") 12 | add_subdirectory(example) 13 | endif () 14 | 15 | option(BUILD_TEST "Whether or not to build the tests" ON) 16 | if (${BUILD_TEST}) 17 | message(STATUS "Building tests") 18 | enable_testing() 19 | add_subdirectory(test) 20 | endif () -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 keith2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OPCODE_EXT.md: -------------------------------------------------------------------------------- 1 | ## GLSL.std.450 2 | 3 | | Opcode | Status | 4 | |-----------------------|--------| 5 | | Round | ✅DONE | 6 | | RoundEven | ✅DONE | 7 | | Trunc | ✅DONE | 8 | | FAbs | ✅DONE | 9 | | SAbs | ✅DONE | 10 | | FSign | ✅DONE | 11 | | SSign | ✅DONE | 12 | | Floor | ✅DONE | 13 | | Ceil | ✅DONE | 14 | | Fract | ✅DONE | 15 | | Radians | ✅DONE | 16 | | Degrees | ✅DONE | 17 | | Sin | ✅DONE | 18 | | Cos | ✅DONE | 19 | | Tan | ✅DONE | 20 | | Asin | ✅DONE | 21 | | Acos | ✅DONE | 22 | | Atan | ✅DONE | 23 | | Sinh | ✅DONE | 24 | | Cosh | ✅DONE | 25 | | Tanh | ✅DONE | 26 | | Asinh | ✅DONE | 27 | | Acosh | ✅DONE | 28 | | Atanh | ✅DONE | 29 | | Atan2 | ✅DONE | 30 | | Pow | ✅DONE | 31 | | Exp | ✅DONE | 32 | | Log | ✅DONE | 33 | | Exp2 | ✅DONE | 34 | | Log2 | ✅DONE | 35 | | Sqrt | ✅DONE | 36 | | InverseSqrt | ✅DONE | 37 | | Determinant | 🟥TODO | 38 | | MatrixInverse | 🟥TODO | 39 | | Modf | ✅DONE | 40 | | ModfStruct | ✅DONE | 41 | | FMin | ✅DONE | 42 | | UMin | ✅DONE | 43 | | SMin | ✅DONE | 44 | | FMax | ✅DONE | 45 | | UMax | ✅DONE | 46 | | SMax | ✅DONE | 47 | | FClamp | ✅DONE | 48 | | UClamp | ✅DONE | 49 | | SClamp | ✅DONE | 50 | | FMix | ✅DONE | 51 | | IMix | 🟥TODO | 52 | | Step | ✅DONE | 53 | | SmoothStep | ✅DONE | 54 | | Fma | ✅DONE | 55 | | Frexp | ✅DONE | 56 | | FrexpStruct | ✅DONE | 57 | | Ldexp | ✅DONE | 58 | | PackSnorm4x8 | ✅DONE | 59 | | PackUnorm4x8 | ✅DONE | 60 | | PackSnorm2x16 | ✅DONE | 61 | | PackUnorm2x16 | ✅DONE | | 62 | | PackHalf2x16 | 🟥TODO | 63 | | PackDouble2x32 | 🟥TODO | 64 | | UnpackSnorm2x16 | ✅DONE | 65 | | UnpackUnorm2x16 | ✅DONE | 66 | | UnpackHalf2x16 | 🟥TODO | 67 | | UnpackSnorm4x8 | ✅DONE | 68 | | UnpackUnorm4x8 | ✅DONE | 69 | | UnpackDouble2x32 | 🟥TODO | 70 | | Length | ✅DONE | 71 | | Distance | ✅DONE | 72 | | Cross | ✅DONE | 73 | | Normalize | ✅DONE | 74 | | FaceForward | ✅DONE | 75 | | Reflect | ✅DONE | 76 | | Refract | ✅DONE | 77 | | FindILsb | ✅DONE | 78 | | FindSMsb | ✅DONE | 79 | | FindUMsb | ✅DONE | 80 | | InterpolateAtCentroid | 🟥TODO | 81 | | InterpolateAtSample | 🟥TODO | 82 | | InterpolateAtOffset | 🟥TODO | 83 | | NMin | ✅DONE | 84 | | NMax | ✅DONE | 85 | | NClamp | ✅DONE | 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPVM 2 | Tiny C++ [SPIR-V](https://registry.khronos.org/SPIR-V/) virtual machine (interpreter), you can use it to debug shaders: first compile your shader code(GLSL/HLSL) to SPIR-V binary file (using tools such as [glslangValidator](https://github.com/KhronosGroup/glslang)), then decode and execute entry point function `main` with SPVM, and check the output. 3 | 4 | [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE) 5 | [![CMake Linux](https://github.com/keith2018/spvm/actions/workflows/cmake_linux.yml/badge.svg)](https://github.com/keith2018/spvm/actions/workflows/cmake_linux.yml) 6 | [![CMake MacOS](https://github.com/keith2018/spvm/actions/workflows/cmake_macos.yml/badge.svg)](https://github.com/keith2018/spvm/actions/workflows/cmake_macos.yml) 7 | [![CMake Windows](https://github.com/keith2018/spvm/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/keith2018/spvm/actions/workflows/cmake_windows.yml) 8 | 9 | Specifications that the project follows is: 10 | - [SPIR-V 1.0](https://registry.khronos.org/SPIR-V/specs/1.0/SPIRV.html) 11 | - [GLSL.std.450](https://registry.khronos.org/SPIR-V/specs/1.0/GLSL.std.450.html) 12 | 13 | ### Limits 14 | - Only part of SPIR-V 1.0 instructions has been implemented right now, see the opcodes support status: 15 | - [Core (SPIR-V 1.0) Opcodes](OPCODE_CORE.md) 16 | - [Ext (GLSL.std.450) Opcodes](OPCODE_EXT.md) 17 | - Only support 32-bits width [Numerical type](https://registry.khronos.org/SPIR-V/specs/1.0/SPIRV.html#_types) (float, integer) 18 | - Only support [Addressing Model](https://registry.khronos.org/SPIR-V/specs/1.0/SPIRV.html#Addressing_Model) `Logical` 19 | - Not support derivative opcodes (dFdx\dFdy\...) 20 | - Not support OpenCL related instructions 21 | 22 | ### TODO 23 | - [ ] 64-bit float/integer support 24 | - [ ] Performance optimizations 25 | - [ ] SIMD 26 | - [ ] JIT/AOT 27 | 28 | The project is still working in progress ... 29 | 30 | ## Spvm-ShaderToy 31 | Spvm-ShaderToy simulated the runtime environment of [shadertoy](https://www.shadertoy.com/), and execute shader code using SPVM (may very slow 😀). 32 | 33 | ### Gallery 34 | 35 |

36 | 37 | 38 |

39 | 40 |

41 | 42 | 43 |

44 | 45 | ## Example 46 | 47 | GLSL fragment shader (see [example/shaders/simple.frag](example/shaders/simple.frag)) 48 | 49 | ```glsl 50 | #version 450 51 | 52 | layout (location = 0) in vec3 inColor; 53 | layout (location = 0) out vec4 outFragColor; 54 | 55 | void main() 56 | { 57 | outFragColor = vec4(inColor.yxz, 1.0f); 58 | } 59 | ``` 60 | 61 | run with spvm (see [example/main.cpp](example/main.cpp)) 62 | 63 | ```cpp 64 | #define HEAP_SIZE 128 * 1024 65 | const char *SPV_PATH = "shaders/simple.frag.spv"; 66 | 67 | SPVM::SpvmModule module; 68 | SPVM::Runtime runtime; 69 | 70 | // decode spir-v file 71 | bool success = SPVM::Decoder::decodeFile(SPV_PATH, &module); 72 | if (!success) { 73 | std::cout << "error decode spir-v file"; 74 | return -1; 75 | } 76 | 77 | // init runtime 78 | success = runtime.initWithModule(&module, HEAP_SIZE); 79 | if (!success) { 80 | std::cout << "error init module"; 81 | return -1; 82 | } 83 | 84 | // get uniform locations 85 | SPVM::SpvmWord inColorLoc = runtime.getLocationByName("inColor"); 86 | SPVM::SpvmWord outFragColorLoc = runtime.getLocationByName("outFragColor"); 87 | 88 | // write input 89 | float inColor[3]{0.2f, 0.3f, 0.4f}; 90 | runtime.writeInput(inColor, inColorLoc); 91 | 92 | // execute shader entry function 'main' 93 | success = runtime.execEntryPoint(); 94 | if (!success) { 95 | std::cout << "error exec entrypoint function"; 96 | return -1; 97 | } 98 | 99 | // read output 100 | float outFragColor[4]; 101 | runtime.readOutput(outFragColor, outFragColorLoc); 102 | 103 | std::cout << "outFragColor[0]: " << outFragColor[0] << std::endl; 104 | std::cout << "outFragColor[1]: " << outFragColor[1] << std::endl; 105 | std::cout << "outFragColor[2]: " << outFragColor[2] << std::endl; 106 | std::cout << "outFragColor[3]: " << outFragColor[3] << std::endl; 107 | ``` 108 | 109 | ## Clone 110 | ```bash 111 | git clone --recursive https://github.com/keith2018/spvm 112 | cd spvm 113 | ``` 114 | 115 | ## Build 116 | ```bash 117 | mkdir build 118 | cmake -B ./build -DCMAKE_BUILD_TYPE=Release 119 | cmake --build ./build --config Release 120 | ``` 121 | 122 | ## Test 123 | ```bash 124 | cd build 125 | ctest 126 | ``` 127 | 128 | ## License 129 | This code is licensed under the MIT License (see [LICENSE](LICENSE)). 130 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3) 2 | project(spvm_example) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | include_directories("../src") 7 | add_executable(${PROJECT_NAME} main.cpp) 8 | 9 | set(LIBRARIES spvm_lib) 10 | target_link_libraries(${PROJECT_NAME} ${LIBRARIES}) 11 | 12 | if (MSVC) 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest") 14 | endif () 15 | 16 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../bin) 17 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../bin) 18 | 19 | # copy assets 20 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 21 | COMMAND ${CMAKE_COMMAND} -E remove_directory $/shaders 22 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/shaders $/shaders 23 | ) -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include 8 | 9 | #include "decoder.h" 10 | #include "runtime.h" 11 | 12 | #define HEAP_SIZE 128 * 1024 13 | const char *SPV_PATH = "shaders/simple.frag.spv"; 14 | 15 | int main(int argc, char *argv[]) { 16 | SPVM::SpvmModule module; 17 | SPVM::Runtime runtime; 18 | 19 | // decode spir-v file 20 | bool success = SPVM::Decoder::decodeFile(SPV_PATH, &module); 21 | if (!success) { 22 | std::cout << "error decode spir-v file"; 23 | return -1; 24 | } 25 | 26 | // init runtime 27 | success = runtime.initWithModule(&module, HEAP_SIZE); 28 | if (!success) { 29 | std::cout << "error init module"; 30 | return -1; 31 | } 32 | 33 | // get uniform locations 34 | SPVM::SpvmWord inColorLoc = runtime.getLocationByName("inColor"); 35 | SPVM::SpvmWord outFragColorLoc = runtime.getLocationByName("outFragColor"); 36 | 37 | // write input 38 | float inColor[3]{0.2f, 0.3f, 0.4f}; 39 | runtime.writeInput(inColor, inColorLoc); 40 | 41 | // execute shader entry function 'main' 42 | success = runtime.execEntryPoint(); 43 | if (!success) { 44 | std::cout << "error exec entrypoint function"; 45 | return -1; 46 | } 47 | 48 | // read output 49 | float outFragColor[4]; 50 | runtime.readOutput(outFragColor, outFragColorLoc); 51 | 52 | std::cout << "outFragColor[0]: " << outFragColor[0] << std::endl; 53 | std::cout << "outFragColor[1]: " << outFragColor[1] << std::endl; 54 | std::cout << "outFragColor[2]: " << outFragColor[2] << std::endl; 55 | std::cout << "outFragColor[3]: " << outFragColor[3] << std::endl; 56 | 57 | return 0; 58 | } -------------------------------------------------------------------------------- /example/shaders/simple.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout (location = 0) in vec3 inColor; 4 | layout (location = 0) out vec4 outFragColor; 5 | 6 | void main() 7 | { 8 | outFragColor = vec4(inColor.yxz, 1.0f); 9 | } 10 | -------------------------------------------------------------------------------- /example/shaders/simple.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/example/shaders/simple.frag.spv -------------------------------------------------------------------------------- /example/shaders/simple.frag.spv.txt: -------------------------------------------------------------------------------- 1 | .\location.frag 2 | // Module Version 10000 3 | // Generated by (magic number): 8000a 4 | // Id's are bound by 20 5 | 6 | Capability Shader 7 | 1: ExtInstImport "GLSL.std.450" 8 | MemoryModel Logical GLSL450 9 | EntryPoint Fragment 4 "main" 9 12 10 | ExecutionMode 4 OriginUpperLeft 11 | Source GLSL 450 12 | Name 4 "main" 13 | Name 9 "outFragColor" 14 | Name 12 "inColor" 15 | Decorate 9(outFragColor) Location 0 16 | Decorate 12(inColor) Location 0 17 | 2: TypeVoid 18 | 3: TypeFunction 2 19 | 6: TypeFloat 32 20 | 7: TypeVector 6(float) 4 21 | 8: TypePointer Output 7(fvec4) 22 | 9(outFragColor): 8(ptr) Variable Output 23 | 10: TypeVector 6(float) 3 24 | 11: TypePointer Input 10(fvec3) 25 | 12(inColor): 11(ptr) Variable Input 26 | 15: 6(float) Constant 1065353216 27 | 4(main): 2 Function None 3 28 | 5: Label 29 | 13: 10(fvec3) Load 12(inColor) 30 | 14: 10(fvec3) VectorShuffle 13 13 1 0 2 31 | 16: 6(float) CompositeExtract 14 0 32 | 17: 6(float) CompositeExtract 14 1 33 | 18: 6(float) CompositeExtract 14 2 34 | 19: 7(fvec4) CompositeConstruct 16 17 18 15 35 | Store 9(outFragColor) 19 36 | Return 37 | FunctionEnd 38 | -------------------------------------------------------------------------------- /images/Nt3BzM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/images/Nt3BzM.png -------------------------------------------------------------------------------- /images/XdlSDs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/images/XdlSDs.png -------------------------------------------------------------------------------- /images/fstyD4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/images/fstyD4.png -------------------------------------------------------------------------------- /images/ftdfWn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/images/ftdfWn.png -------------------------------------------------------------------------------- /shadertoy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3) 2 | project(spvm_shadertoy) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | set(THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party") 6 | 7 | include_directories( 8 | "${THIRD_PARTY_DIR}/glfw/include" 9 | "${THIRD_PARTY_DIR}/glad/include" 10 | "${THIRD_PARTY_DIR}/imgui" 11 | "${THIRD_PARTY_DIR}/stb/include" 12 | "${THIRD_PARTY_DIR}/glslang/include" 13 | "../src" 14 | ) 15 | 16 | set(SKIP_GLSLANG_INSTALL ON) 17 | set(ENABLE_GLSLANG_BINARIES OFF) 18 | set(ENABLE_CTEST OFF) 19 | set(BUILD_TESTING OFF) 20 | add_subdirectory("${THIRD_PARTY_DIR}/glslang") 21 | 22 | # imgui src 23 | file(GLOB IMGUI_SRC 24 | ${THIRD_PARTY_DIR}/imgui/imgui/*.cpp 25 | ) 26 | 27 | add_executable(${PROJECT_NAME} 28 | "${IMGUI_SRC}" 29 | "${THIRD_PARTY_DIR}/glad/src/glad.c" 30 | renderer.cpp 31 | compiler.cpp 32 | resourcelimits.cpp 33 | settingpanel.cpp 34 | timer.cpp 35 | main.cpp 36 | ) 37 | 38 | set(LIBRARIES 39 | spvm_lib 40 | glslang 41 | SPIRV) 42 | 43 | if (WIN32) 44 | if (MSVC) 45 | target_link_libraries(${PROJECT_NAME} 46 | ${LIBRARIES} 47 | "${THIRD_PARTY_DIR}/glfw/lib-vc2022/glfw3.lib" 48 | "${THIRD_PARTY_DIR}/glfw/lib-vc2022/glfw3dll.lib" 49 | ) 50 | else () 51 | target_link_libraries(${PROJECT_NAME} 52 | ${LIBRARIES} 53 | "${THIRD_PARTY_DIR}/glfw/lib-mingw-w64/libglfw3.a" 54 | "${THIRD_PARTY_DIR}/glfw/lib-mingw-w64/libglfw3dll.a" 55 | ) 56 | endif () 57 | endif () 58 | 59 | if (APPLE) 60 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -framework Cocoa -framework OpenGL -framework IOKit") 61 | add_compile_definitions(GL_SILENCE_DEPRECATION) 62 | target_link_libraries(${PROJECT_NAME} 63 | ${LIBRARIES} 64 | "${THIRD_PARTY_DIR}/glfw/lib-macos-universal/libglfw3.a" 65 | ) 66 | endif () 67 | 68 | if (UNIX AND NOT APPLE) 69 | find_package(OpenGL REQUIRED) 70 | target_link_libraries(${PROJECT_NAME} 71 | ${LIBRARIES} 72 | glfw 73 | OpenGL::GL 74 | pthread 75 | ${CMAKE_DL_LIBS} 76 | ) 77 | endif () 78 | 79 | if (MSVC) 80 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest") 81 | else () 82 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") 83 | endif () 84 | 85 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../bin) 86 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../bin) 87 | 88 | # copy assets 89 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 90 | COMMAND ${CMAKE_COMMAND} -E remove_directory $/assets 91 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets $/assets 92 | ) -------------------------------------------------------------------------------- /shadertoy/assets/images/awesomeface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/assets/images/awesomeface.png -------------------------------------------------------------------------------- /shadertoy/assets/images/wood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/assets/images/wood.png -------------------------------------------------------------------------------- /shadertoy/assets/shaders/Nt3BzM.frag: -------------------------------------------------------------------------------- 1 | // from https://www.shadertoy.com/view/Nt3BzM 2 | 3 | #define cx_arg(a) atan(a.y, a.x) 4 | 5 | 6 | #define PI 3.14159265 7 | #define TAU (2.*PI) 8 | 9 | #define cx_mul(a, b) vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x) 10 | #define cx_div(a, b) vec2(((a.x*b.x + a.y*b.y)/(b.x*b.x + b.y*b.y)),((a.y*b.x - a.x*b.y)/(b.x*b.x + b.y*b.y))) 11 | #define cx_sin(a) vec2(sin(a.x) * cosh(a.y), cos(a.x) * sinh(a.y)) 12 | #define cx_cos(a) vec2(cos(a.x) * cosh(a.y), -sin(a.x) * sinh(a.y)) 13 | 14 | float sharp = 0.39; // delay 15 | float b = 0.655; // brightness 0 -> dark, 1 -> bright 16 | float nMod = 2.; // num of level curves mod 17 | float nPhase = 20.; // num. of level curves phase 18 | float base = 2.; // For the base of a logarithm 19 | float nContour = 16.; 20 | 21 | vec2 as_polar(vec2 z) { 22 | return vec2( 23 | length(z), 24 | atan(z.y, z.x) 25 | ); 26 | } 27 | 28 | vec2 cx_tan(vec2 a) {return cx_div(cx_sin(a), cx_cos(a)); } 29 | vec2 cx_log(vec2 a) { 30 | vec2 polar = as_polar(a); 31 | float rpart = polar.x; 32 | float ipart = polar.y; 33 | if (ipart > PI) ipart=ipart-(2.0*PI); 34 | return vec2(log(rpart),ipart); 35 | } 36 | vec2 cx_pow(vec2 v, float p) { 37 | vec2 z = as_polar(v); 38 | return pow(z.x, p) * vec2(cos(z.y * p), sin(z.y * p)); 39 | } 40 | 41 | vec3 rgb2hsv(vec3 c) 42 | { 43 | vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); 44 | vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); 45 | vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); 46 | 47 | float d = q.x - min(q.w, q.y); 48 | float e = 1.0e-10; 49 | return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); 50 | } 51 | 52 | vec3 hsv2rgb(vec3 c) 53 | { 54 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 55 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 56 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 57 | } 58 | 59 | vec3 palette( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d ) 60 | { 61 | return a + b*cos(2. * PI *(c*t+d) ); 62 | } 63 | 64 | float funPhase(vec2 p) { 65 | return (PI - atan(p.y, -p.x)) / (2. * PI); 66 | } 67 | 68 | float funColor(vec2 p) { 69 | return sharp * (nContour * (PI - atan(p.y, -p.x)) / (2. * PI) - floor(nContour * (PI - atan(p.y, -p.x)) / (2. * PI))) + 0.7; 70 | } 71 | 72 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 73 | { 74 | // Normalized pixel coordinates (from 0 to 1) 75 | vec2 uv = (fragCoord.xy - 0.5 * iResolution.xy) / min(iResolution.y, iResolution.x); 76 | vec2 z = uv; 77 | float angle = sin(iTime/5.) * TAU; 78 | float length = .2; 79 | 80 | // Spin our points in a circle of radius length 81 | float c = cos(angle); 82 | float s = sin(angle); 83 | vec2 p = vec2( s*length, c*length); 84 | vec2 q = vec2( s*-length, c*-length ); 85 | 86 | //(z-1)/(z^3+z+1) 87 | 88 | vec2 z_minus_one = z - vec2(sin(iTime / 2.), cos(iTime)); 89 | vec2 z_cubed_plus_one = cx_mul(cx_mul(z, z), z) + z * (sin(iTime / 10.) + 0.1) - vec2(1, 0); 90 | vec2 division = cx_div(z_minus_one, z_cubed_plus_one); 91 | 92 | 93 | vec3 col = palette( funPhase(division) * (8. * sin(iTime / 10.)), vec3(0.50,.52,0.53), vec3(.46,.32,.35), vec3(.82,.84,.65), vec3(0.53,0.23,0.22)); 94 | vec3 hsv = rgb2hsv(col); 95 | col = vec3(hsv.r, hsv.b, funColor(division)); 96 | //vec3 col = vec3(funPhase(division), 1., funColor(division)); 97 | fragColor = vec4(col, 1.0); 98 | } -------------------------------------------------------------------------------- /shadertoy/assets/shaders/XdlSDs.frag: -------------------------------------------------------------------------------- 1 | // from https://www.shadertoy.com/view/XdlSDs 2 | 3 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 4 | { 5 | vec2 p = (2.0*fragCoord.xy-iResolution.xy)/iResolution.y; 6 | float tau = 3.1415926535*2.0; 7 | float a = atan(p.x,p.y); 8 | float r = length(p)*0.75; 9 | vec2 uv = vec2(a/tau,r); 10 | 11 | //get the color 12 | float xCol = (uv.x - (iTime / 3.0)) * 3.0; 13 | xCol = mod(xCol, 3.0); 14 | vec3 horColour = vec3(0.25, 0.25, 0.25); 15 | 16 | if (xCol < 1.0) { 17 | 18 | horColour.r += 1.0 - xCol; 19 | horColour.g += xCol; 20 | } 21 | else if (xCol < 2.0) { 22 | 23 | xCol -= 1.0; 24 | horColour.g += 1.0 - xCol; 25 | horColour.b += xCol; 26 | } 27 | else { 28 | 29 | xCol -= 2.0; 30 | horColour.b += 1.0 - xCol; 31 | horColour.r += xCol; 32 | } 33 | 34 | // draw color beam 35 | uv = (2.0 * uv) - 1.0; 36 | float beamWidth = (0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iTime)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y)); 37 | vec3 horBeam = vec3(beamWidth); 38 | fragColor = vec4((( horBeam) * horColour), 1.0); 39 | } -------------------------------------------------------------------------------- /shadertoy/assets/shaders/basic.frag: -------------------------------------------------------------------------------- 1 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 2 | { 3 | // Normalized pixel coordinates (from 0 to 1) 4 | vec2 uv = fragCoord/iResolution.xy; 5 | 6 | // Time varying pixel color 7 | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); 8 | 9 | // Output to screen 10 | fragColor = vec4(col,1.0); 11 | } -------------------------------------------------------------------------------- /shadertoy/assets/shaders/fstyD4.frag: -------------------------------------------------------------------------------- 1 | // from https://www.shadertoy.com/view/fstyD4 2 | 3 | // Author: bitless 4 | // Title: Coastal Landscape 5 | 6 | // Thanks to Patricio Gonzalez Vivo & Jen Lowe for "The Book of Shaders" 7 | // and Fabrice Neyret (FabriceNeyret2) for https://shadertoyunofficial.wordpress.com/ 8 | // and Inigo Quilez (iq) for https://iquilezles.org/www/index.htm 9 | // and whole Shadertoy community for inspiration. 10 | 11 | #define p(t, a, b, c, d) ( a + b*cos( 6.28318*(c*t+d) ) ) //IQ's palette function (https://www.iquilezles.org/www/articles/palettes/palettes.htm) 12 | #define sp(t) p(t,vec3(.26,.76,.77),vec3(1,.3,1),vec3(.8,.4,.7),vec3(0,.12,.54)) //sky palette 13 | #define hue(v) ( .6 + .76 * cos(6.3*(v) + vec4(0,23,21,0) ) ) //hue 14 | 15 | // "Hash without Sine" by Dave_Hoskins. 16 | // https://www.shadertoy.com/view/4djSRW 17 | float hash12(vec2 p) 18 | { 19 | vec3 p3 = fract(vec3(p.xyx) * .1031); 20 | p3 += dot(p3, p3.yzx + 33.33); 21 | return fract((p3.x + p3.y) * p3.z); 22 | } 23 | 24 | vec2 hash22(vec2 p) 25 | { 26 | vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973)); 27 | p3 += dot(p3, p3.yzx+33.33); 28 | return fract((p3.xx+p3.yz)*p3.zy); 29 | } 30 | //////////////////////// 31 | 32 | vec2 rotate2D (vec2 st, float a){ 33 | return mat2(cos(a),-sin(a),sin(a),cos(a))*st; 34 | } 35 | 36 | float st(float a, float b, float s) //AA bar 37 | { 38 | return smoothstep (a-s, a+s, b); 39 | } 40 | 41 | float noise( in vec2 p ) //gradient noise 42 | { 43 | vec2 i = floor( p ); 44 | vec2 f = fract( p ); 45 | 46 | vec2 u = f*f*(3.-2.*f); 47 | 48 | return mix( mix( dot( hash22( i+vec2(0,0) ), f-vec2(0,0) ), 49 | dot( hash22( i+vec2(1,0) ), f-vec2(1,0) ), u.x), 50 | mix( dot( hash22( i+vec2(0,1) ), f-vec2(0,1) ), 51 | dot( hash22( i+vec2(1,1) ), f-vec2(1,1) ), u.x), u.y); 52 | } 53 | 54 | void mainImage( out vec4 O, in vec2 g) 55 | { 56 | vec2 r = iResolution.xy 57 | ,uv = (g+g-r)/r.y 58 | ,sun_pos = vec2(r.x/r.y*.42,-.53) //sun position 59 | ,tree_pos = vec2(-r.x/r.y*.42,-.2) //tree position 60 | ,sh, u, id, lc, t; 61 | 62 | vec3 f, c; 63 | float xd, yd, h, a, l; 64 | vec4 C; 65 | 66 | float sm = 3./r.y; //smoothness factor for AA 67 | 68 | sh = rotate2D(sun_pos, noise(uv+iTime*.25)*.3); //big noise on the sky 69 | 70 | if (uv.y > -.4) //drawing the sky 71 | { 72 | u = uv + sh; 73 | 74 | yd = 60.; //number of rings 75 | 76 | id = vec2((length(u)+.01)*yd,0); //segment id: x - ring number, y - segment number in the ring 77 | xd = floor(id.x)*.09; //number of ring segments 78 | h = (hash12(floor(id.xx))*.5+.25)*(iTime+10.)*.25; //ring shift 79 | t = rotate2D (u,h); //rotate the ring to the desired angle 80 | 81 | id.y = atan(t.y,t.x)*xd; 82 | lc = fract(id); //segment local coordinates 83 | id -= lc; 84 | 85 | // determining the coordinates of the center of the segment in uv space 86 | t = vec2(cos((id.y+.5)/xd)*(id.x+.5)/yd,sin((id.y+.5)/xd)*(id.x+.5)/yd); 87 | t = rotate2D(t,-h) - sh; 88 | 89 | h = noise(t*vec2(.5,1)-vec2(iTime*.2,0)) //clouds 90 | * step(-.25,t.y); //do not draw clouds below -.25 91 | h = smoothstep (.052,.055, h); 92 | 93 | 94 | lc += (noise(lc*vec2(1,4)+id))*vec2(.7,.2); //add fine noise 95 | 96 | f = mix (sp(sin(length(u)-.1))*.35, //sky background 97 | mix(sp(sin(length(u)-.1)+(hash12(id)-.5)*.15),vec3(1),h), //mix sky color and clouds 98 | st(abs(lc.x-.5),.4,sm*yd)*st(abs(lc.y-.5),.48,sm*xd)); 99 | }; 100 | 101 | if (uv.y < -.35) //drawing water 102 | { 103 | 104 | float cld = noise(-sh*vec2(.5,1) - vec2(iTime*.2,0)); //cloud density opposite the center of the sun 105 | cld = 1.- smoothstep(.0,.15,cld)*.5; 106 | 107 | u = uv*vec2(1,15); 108 | id = floor(u); 109 | 110 | for (float i = 1.; i > -1.; i--) //drawing a wave and its neighbors from above and below 111 | { 112 | if (id.y+i < -5.) 113 | { 114 | lc = fract(u)-.5; 115 | lc.y = (lc.y+(sin(uv.x*12.-iTime*3.+id.y+i))*.25-i)*4.; //set the waveform and divide it into four strips 116 | h = hash12(vec2(id.y+i,floor(lc.y))); //the number of segments in the strip and its horizontal offset 117 | 118 | xd = 6.+h*4.; 119 | yd = 30.; 120 | lc.x = uv.x*xd+sh.x*9.; //divide the strip into segments 121 | lc.x += sin(iTime * (.5 + h*2.))*.5; //add a cyclic shift of the strips horizontally 122 | h = .8*smoothstep(5.,.0,abs(floor(lc.x)))*cld+.1; //determine brightness of the sun track 123 | f = mix(f,mix(vec3(0,.1,.5),vec3(.35,.35,0),h),st(lc.y,0.,sm*yd)); //mix the color of the water and the color of the track for the background of the water 124 | lc += noise(lc*vec2(3,.5))*vec2(.1,.6); //add fine noise to the segment 125 | 126 | f = mix(f, //mix the background color 127 | mix(hue(hash12(floor(lc))*.1+.56).rgb*(1.2+floor(lc.y)*.17),vec3(1,1,0),h) //and the stroke color 128 | ,st(lc.y,0.,sm*xd) 129 | *st(abs(fract(lc.x)-.5),.48,sm*xd)*st(abs(fract(lc.y)-.5),.3,sm*yd) 130 | ); 131 | } 132 | } 133 | } 134 | 135 | O = vec4(f,1); 136 | 137 | ////////////////////// drawing the grass 138 | a = 0.; 139 | u = uv+noise(uv*2.)*.1 + vec2(0,sin(uv.x*1.+3.)*.4+.8); 140 | 141 | f = mix(vec3(.7,.6,.2),vec3(0,1,0),sin(iTime*.2)*.5+.5); //color of the grass, changing from green to yellow and back again 142 | O = mix(O,vec4(f*.4,1),step(u.y,.0)); //draw grass background 143 | 144 | xd = 60.; //grass size 145 | u = u*vec2(xd,xd/3.5); 146 | 147 | 148 | if (u.y < 1.2) 149 | { 150 | for (float y = 0.; y > -3.; y--) 151 | { 152 | for (float x = -2.; x <3.; x++) 153 | { 154 | id = floor(u) + vec2(x,y); 155 | lc = (fract(u) + vec2(1.-x,-y))/vec2(5,3); 156 | h = (hash12(id)-.5)*.25+.5; //shade and length for an individual blade of grass 157 | 158 | lc-= vec2(.3,.5-h*.4); 159 | lc.x += sin(((iTime*1.7+h*2.-id.x*.05-id.y*.05)*1.1+id.y*.5)*2.)*(lc.y+.5)*.5; 160 | t = abs(lc)-vec2(.02,.5-h*.5); 161 | l = length(max(t,0.)) + min(max(t.x,t.y),0.); //distance to the segment (blade of grass) 162 | 163 | l -= noise (lc*7.+id)*.1; //add fine noise 164 | C = vec4(f*.25,st(l,.1,sm*xd*.09)); //grass outline 165 | C = mix(C,vec4(f //grass foregroud 166 | *(1.2+lc.y*2.) //the grass is a little darker at the root 167 | *(1.8-h*2.5),1.) //brightness variations for individual blades of grass 168 | ,st(l,.04,sm*xd*.09)); 169 | 170 | O = mix (O,C,C.a*step (id.y,-1.)); 171 | a = max (a, C.a*step (id.y,-5.)); //a mask to cover the trunk of the tree with grasses in the foreground 172 | } 173 | } 174 | } 175 | 176 | float T = sin(iTime*.5); //tree swing cycle 177 | 178 | if (abs(uv.x+tree_pos.x-.1-T*.1) < .6) // drawing the tree 179 | { 180 | u = uv + tree_pos; 181 | // draw the trunk of the tree first 182 | u.x -= sin(u.y+1.)*.2*(T+.75); //the trunk bends in the wind 183 | u += noise(u*4.5-7.)*.25; //trunk curvature 184 | 185 | xd = 10., yd = 60.; 186 | t = u * vec2(1,yd); //divide the trunk into segments 187 | h = hash12(floor(t.yy)); //horizontal shift of the segments and the color tint of the segment 188 | t.x += h*.01; 189 | t.x *= xd; 190 | 191 | lc = fract(t); //segment local coordinates 192 | 193 | float m = st(abs(t.x-.5),.5,sm*xd)*step(abs(t.y+20.),45.); //trunk mask 194 | C = mix(vec4(.07) //outline color 195 | ,vec4(.5,.3,0,1)*(.4+h*.4) //foreground color 196 | ,st(abs(lc.y-.5),.4,sm*yd)*st(abs(lc.x-.5),.45,sm*xd)); 197 | C.a = m; 198 | 199 | xd = 30., yd = 15.; 200 | 201 | for (float xs =0.;xs<4.;xs++) //drawing four layers of foliage 202 | { 203 | u = uv + tree_pos + vec2 (xs/xd*.5 -(T +.75)*.15,-.7); //crown position 204 | u += noise(u*vec2(2,1)+vec2(-iTime+xs*.05,0))*vec2(-.25,.1)*smoothstep (.5,-1.,u.y+.7)*.75; //leaves rippling in the wind 205 | 206 | t = u * vec2(xd,1.); 207 | h = hash12(floor(t.xx)+xs*1.4); //number of segments for the row 208 | 209 | yd = 5.+ h*7.; 210 | t.y *= yd; 211 | 212 | sh = t; 213 | lc = fract(t); 214 | h = hash12(t-lc); //segment color shade 215 | 216 | 217 | t = (t-lc)/vec2(xd,yd)+vec2(0,.7); 218 | 219 | m = (step(0.,t.y)*step (length(t),.45) //the shape of the crown - the top 220 | + step (t.y,0.)*step (-0.7+sin((floor(u.x)+xs*.5)*15.)*.2,t.y)) //the bottom 221 | *step (abs(t.x),.5) //crown size horizontally 222 | *st(abs(lc.x-.5),.35,sm*xd*.5); 223 | 224 | lc += noise((sh)*vec2(1.,3.))*vec2(.3,.3); //add fine noise 225 | 226 | f = hue((h+(sin(iTime*.2)*.5+.5))*.2).rgb-t.x; //color of the segment changes cyclically 227 | 228 | C = mix(C, 229 | vec4(mix(f*.15,f*.6*(.7+xs*.2), //mix outline and foreground color 230 | st(abs(lc.y-.5),.47,sm*yd)*st(abs(lc.x-.5),.2,sm*xd)),m) 231 | ,m); 232 | } 233 | 234 | O = mix (O,C,C.a*(1.-a)); 235 | } 236 | } -------------------------------------------------------------------------------- /shadertoy/assets/shaders/ftdfWn.frag: -------------------------------------------------------------------------------- 1 | // from https://www.shadertoy.com/view/ftdfWn 2 | 3 | #define ITER_MAX 13 4 | #define TIME_SCALE 6 5 | #define ZOOM 4 6 | //#define CUSTOM_EXPONENT vec2(-2., 0.) 7 | 8 | 9 | 10 | // conversions between cartesian (x+yi) and polar (xe^(iy)) forms of complex numbers 11 | vec2 cartesian_of_polar(vec2 polar) { 12 | return vec2(polar.x * cos(polar.y), polar.x * sin(polar.y)); 13 | } 14 | 15 | vec2 polar_of_cartesian(vec2 cartesian) { 16 | return vec2(length(cartesian), atan(cartesian.y, cartesian.x)); 17 | } 18 | 19 | // multiplication of two complex numbers in cartesian form 20 | vec2 cmul(vec2 a, vec2 b) { 21 | return vec2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); 22 | } 23 | 24 | // exponential of a complex number by a complex number, both in cartesian form 25 | vec2 cexp(vec2 b, vec2 e) { 26 | vec2 b_polar = polar_of_cartesian(b); 27 | vec2 logb = vec2(log(b_polar.x), b_polar.y); 28 | vec2 new_e = cmul(logb, e); 29 | vec2 ans_polar = vec2(exp(new_e.x), new_e.y); 30 | return cartesian_of_polar(ans_polar); 31 | } 32 | 33 | // cexp, specialized to e = vec2(-2., 0.) 34 | vec2 cexp_m2(vec2 b) { 35 | vec2 b_invmag = b / vec2(dot(b, b)); 36 | return vec2((b_invmag.x*b_invmag.x - b_invmag.y*b_invmag.y), -2.*b_invmag.x*b_invmag.y); 37 | } 38 | 39 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 40 | { 41 | // Normalized pixel coordinates (from -ZOOM to ZOOM) 42 | vec2 uv = vec2(2. * float(ZOOM)) * (fragCoord - iResolution.xy/vec2(2.)) / vec2(max(iResolution.x, iResolution.y)); 43 | 44 | // Animate the constant c 45 | float t = iTime/float(TIME_SCALE); 46 | vec2 c = (vec2(cos(t)*abs(cos(t)), sin(t)*abs(sin(t)))) * vec2(0.7665); 47 | 48 | // Computation of the Julia set defined by the iteration x -> x^-2 + c 49 | vec2 x = uv; 50 | int iter = 0; 51 | for (iter = 0; iter < ITER_MAX; ++iter) { 52 | 53 | if (dot(x, x) > 40.) break; 54 | #ifdef CUSTOM_EXPONENT 55 | x = cexp(x, CUSTOM_EXPONENT); 56 | #else 57 | x = cexp_m2(x); 58 | #endif 59 | x += c; 60 | } 61 | vec3 col = vec3(float(iter) / float(ITER_MAX)) * vec3(c, 1.0); 62 | 63 | // Output to screen 64 | fragColor = vec4(col,1.0); 65 | } 66 | -------------------------------------------------------------------------------- /shadertoy/assets/wrapper.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(set = 0, binding = 0) uniform InputData 4 | { 5 | vec3 iResolution; // viewport resolution (in pixels) 6 | float iTime; // shader playback time (in seconds) 7 | float iTimeDelta; // render time (in seconds) 8 | int iFrame; // shader playback frame 9 | vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click 10 | vec4 iDate; // (year, month, day, time in seconds) 11 | float iSampleRate; // sound sample rate (i.e., 44100) 12 | }; 13 | 14 | layout(set = 0, binding = 1) uniform sampler2D iChannel0; 15 | 16 | layout (location = 0) in vec2 fragCoord; 17 | layout (location = 0) out vec4 fragColor; 18 | 19 | void mainImage(out vec4 fragColor, in vec2 fragCoord); 20 | 21 | void main() 22 | { 23 | mainImage(fragColor, fragCoord); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /shadertoy/compiler.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "compiler.h" 8 | #include "glslang/Include/glslang_c_interface.h" 9 | #include "glslang/Public/ShaderLang.h" 10 | #include "resourcelimits.h" 11 | #include "logger.h" 12 | 13 | namespace SPVM { 14 | namespace ShaderToy { 15 | 16 | static std::vector compileShaderInternal(glslang_stage_t stage, const char *shaderSource) { 17 | const glslang_input_t input = { 18 | .language = GLSLANG_SOURCE_GLSL, 19 | .stage = stage, 20 | .client = GLSLANG_CLIENT_VULKAN, 21 | .client_version = GLSLANG_TARGET_VULKAN_1_2, 22 | .target_language = GLSLANG_TARGET_SPV, 23 | .target_language_version = GLSLANG_TARGET_SPV_1_5, 24 | .code = shaderSource, 25 | .default_version = 100, 26 | .default_profile = GLSLANG_NO_PROFILE, 27 | .force_default_version_and_profile = false, 28 | .forward_compatible = false, 29 | .messages = GLSLANG_MSG_DEFAULT_BIT, 30 | .resource = reinterpret_cast(&glslang::DefaultTBuiltInResource), 31 | }; 32 | 33 | glslang_shader_t *shader = glslang_shader_create(&input); 34 | 35 | if (!glslang_shader_preprocess(shader, &input)) { 36 | LOGE("GLSL preprocessing failed:"); 37 | LOGE("%s", glslang_shader_get_info_log(shader)); 38 | LOGE("%s", glslang_shader_get_info_debug_log(shader)); 39 | LOGE("%s", input.code); 40 | glslang_shader_delete(shader); 41 | return std::vector(); 42 | } 43 | 44 | if (!glslang_shader_parse(shader, &input)) { 45 | LOGE("GLSL parsing failed:"); 46 | LOGE("%s", glslang_shader_get_info_log(shader)); 47 | LOGE("%s", glslang_shader_get_info_debug_log(shader)); 48 | LOGE("%s", glslang_shader_get_preprocessed_code(shader)); 49 | glslang_shader_delete(shader); 50 | return std::vector(); 51 | } 52 | 53 | glslang_program_t *program = glslang_program_create(); 54 | glslang_program_add_shader(program, shader); 55 | 56 | if (!glslang_program_link(program, GLSLANG_MSG_SPV_RULES_BIT | GLSLANG_MSG_VULKAN_RULES_BIT)) { 57 | LOGE("GLSL linking failed:"); 58 | LOGE("%s", glslang_program_get_info_log(program)); 59 | LOGE("%s", glslang_program_get_info_debug_log(program)); 60 | glslang_program_delete(program); 61 | glslang_shader_delete(shader); 62 | return std::vector(); 63 | } 64 | 65 | glslang_program_SPIRV_generate(program, stage); 66 | 67 | std::vector outShaderModule(glslang_program_SPIRV_get_size(program)); 68 | glslang_program_SPIRV_get(program, outShaderModule.data()); 69 | 70 | const char *spirv_messages = glslang_program_SPIRV_get_messages(program); 71 | if (spirv_messages) { 72 | LOGI("GLSLang: %s", spirv_messages); 73 | } 74 | 75 | glslang_program_delete(program); 76 | glslang_shader_delete(shader); 77 | 78 | return outShaderModule; 79 | } 80 | 81 | std::vector Compiler::compileShaderFragment(const char *shaderSource) { 82 | glslang::InitializeProcess(); 83 | auto ret = compileShaderInternal(GLSLANG_STAGE_FRAGMENT, shaderSource); 84 | glslang::FinalizeProcess(); 85 | return ret; 86 | } 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /shadertoy/compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace SPVM { 13 | namespace ShaderToy { 14 | 15 | class Compiler { 16 | public: 17 | static std::vector compileShaderFragment(const char *shaderSource); 18 | 19 | }; 20 | 21 | } 22 | } -------------------------------------------------------------------------------- /shadertoy/renderer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "renderer.h" 8 | #include "decoder.h" 9 | #include "compiler.h" 10 | #include "utils.h" 11 | #include "logger.h" 12 | 13 | #define STB_IMAGE_IMPLEMENTATION 14 | #include 15 | 16 | #define HEAP_SIZE 512 * 1024 17 | #define PIXEL_CONVERT(val) 255 * fClamp(val, 0.f, 1.f) 18 | #define PIXEL_ROW_PTR(row) (row) >= colorBuffer_->height ? nullptr : &colorBuffer_->data[(row) * colorBuffer_->width * 4] 19 | 20 | const char *IMAGE_0_PATH = "assets/images/awesomeface.png"; 21 | #define SOUND_SAMPLE_RATE 44100 22 | 23 | namespace SPVM { 24 | namespace ShaderToy { 25 | 26 | Renderer::Renderer() : colorBuffer_(nullptr) {} 27 | 28 | Renderer::~Renderer() { 29 | destroy(); 30 | } 31 | 32 | bool Renderer::create(void *window, int width, int height) { 33 | colorBuffer_ = new Buffer2D(); 34 | colorBuffer_->data = nullptr; 35 | createBuffer(width, height); 36 | 37 | // init uniform 38 | uniformInput_.iResolution.x = (float) colorBuffer_->width; 39 | uniformInput_.iResolution.y = (float) colorBuffer_->height; 40 | uniformInput_.iResolution.z = 0; 41 | uniformInput_.iSampleRate = SOUND_SAMPLE_RATE; 42 | updateUniformDate(uniformInput_.iDate); 43 | 44 | int iw = 0, ih = 0, n = 0; 45 | stbi_set_flip_vertically_on_load(true); 46 | unsigned char *pixelBytes = stbi_load(IMAGE_0_PATH, &iw, &ih, &n, STBI_default); 47 | if (pixelBytes != nullptr && n > 0) { 48 | // image 49 | SPVM::SpvmImageInfo imageInfo; 50 | imageInfo.dim = SpvDim2D; 51 | imageInfo.format = SPVM_FORMAT_UNDEFINED; 52 | imageInfo.width = iw; 53 | imageInfo.height = ih; 54 | imageInfo.depth = 1; 55 | imageInfo.mipmaps = true; 56 | imageInfo.baseMipLevel = 0; 57 | imageInfo.mipLevels = 5; 58 | imageInfo.arrayed = false; 59 | imageInfo.baseArrayLayer = 0; 60 | imageInfo.arrayLayers = 1; 61 | imageInfo.samples = 1; 62 | switch (n) { 63 | case 1: 64 | imageInfo.format = SPVM_FORMAT_R8_UNORM; 65 | break; 66 | case 2: 67 | imageInfo.format = SPVM_FORMAT_R8G8_UNORM; 68 | break; 69 | case 3: 70 | imageInfo.format = SPVM_FORMAT_R8G8B8_UNORM; 71 | break; 72 | case 4: 73 | imageInfo.format = SPVM_FORMAT_R8G8B8A8_UNORM; 74 | break; 75 | default: 76 | break; 77 | } 78 | 79 | iChannel0Image_ = SPVM::createImage(&imageInfo); 80 | SPVM::uploadImageData(iChannel0Image_, pixelBytes, iw * ih * n, iw, ih, 1); 81 | stbi_image_free(pixelBytes); 82 | 83 | // mipmaps 84 | generateMipmaps(iChannel0Image_, SpvSamplerFilterModeLinear); 85 | 86 | // sampler 87 | iChannel0Sampler_ = SPVM::createSampler(); 88 | iChannel0Sampler_->info.minFilter = SpvSamplerFilterModeLinear; 89 | iChannel0Sampler_->info.magFilter = SpvSamplerFilterModeLinear; 90 | iChannel0Sampler_->info.mipmapMode = SpvSamplerFilterModeLinear; 91 | 92 | // sampledImage 93 | iChannel0SampledImage_ = SPVM::createSampledImage(iChannel0Image_, iChannel0Sampler_); 94 | } 95 | 96 | // load shader 97 | settings_.loadShaderCallback_ = [&](const std::string &shaderPath) { 98 | reloadShader(shaderPath.c_str()); 99 | }; 100 | shaderWrapperStr_ = readFileString(settings_.shaderWrapperPath_.c_str()); 101 | if (shaderWrapperStr_.empty()) { 102 | LOGE("error read shader wrapper file: %s", settings_.shaderWrapperPath_.c_str()); 103 | return false; 104 | } 105 | 106 | return reloadShader(settings_.getShaderPath().c_str()); 107 | } 108 | 109 | bool Renderer::reloadShader(const char *shaderPath) { 110 | LOGI("start load shader: %s", shaderPath); 111 | std::string shaderStr = readFileString(shaderPath); 112 | if (shaderStr.empty()) { 113 | LOGE("error read shader file: %s", shaderPath); 114 | return false; 115 | } 116 | 117 | shaderStr = shaderWrapperStr_ + shaderStr; 118 | spvBytes_ = Compiler::compileShaderFragment(shaderStr.c_str()); 119 | if (spvBytes_.empty()) { 120 | LOGE("error compile shader file to spir-v"); 121 | return false; 122 | } 123 | 124 | // decode spv file 125 | delete module_; 126 | module_ = new SpvmModule(); 127 | bool success = SPVM::Decoder::decodeBytes((const SpvmByte *) (spvBytes_.data()), 128 | spvBytes_.size() * sizeof(uint32_t), 129 | module_); 130 | if (!success) { 131 | LOGE("decode spv file failed"); 132 | return false; 133 | } 134 | 135 | // init runtime 136 | runtimes_.clear(); 137 | runtimes_.resize(threadPool_.getThreadCnt()); 138 | for (auto &rt : runtimes_) { 139 | success = rt.initWithModule(module_, HEAP_SIZE); 140 | if (!success) { 141 | LOGE("init spvm runtime failed"); 142 | return false; 143 | } 144 | 145 | if (iChannel0SampledImage_) { 146 | rt.writeUniformBinding(iChannel0SampledImage_, 0, 1, 0); 147 | } 148 | } 149 | 150 | lastFrameTime_ = 0.f; 151 | frameIdx_ = 0; 152 | timer_.start(); 153 | 154 | LOGI("load shader success, derivative instructions found: %s.", module_->hasDerivativeOpcodes ? "true" : "false"); 155 | return true; 156 | } 157 | 158 | void Renderer::drawFrame() { 159 | if (colorBuffer_ == nullptr || module_ == nullptr) { 160 | LOGE("drawFrame error: not inited"); 161 | return; 162 | } 163 | 164 | // update uniform 165 | uniformInput_.iTime = (float) timer_.elapse() / 1000.f; 166 | uniformInput_.iTimeDelta = uniformInput_.iTime - lastFrameTime_; 167 | lastFrameTime_ = uniformInput_.iTime; 168 | frameIdx_++; 169 | uniformInput_.iFrame = frameIdx_; 170 | 171 | for (auto &rt : runtimes_) { 172 | rt.writeUniformBinding(&uniformInput_, 0, 0); 173 | } 174 | 175 | // fragment shading 176 | float blockSize = (float) rasterBlockSize_; 177 | int blockCntY = (int) (((float) colorBuffer_->height + blockSize - 1.f) / blockSize); 178 | int blockCntX = (int) (((float) colorBuffer_->width + blockSize - 1.f) / blockSize); 179 | 180 | for (int blockY = 0; blockY < blockCntY; blockY++) { 181 | for (int blockX = 0; blockX < blockCntX; blockX++) { 182 | threadPool_.pushTask([&, blockX, blockY](int threadId) { 183 | execBlockShadingSingle(threadId, blockX, blockY, (int) blockSize); 184 | }); 185 | } 186 | } 187 | 188 | threadPool_.waitTasksFinish(); 189 | } 190 | 191 | void Renderer::execBlockShadingSingle(int threadId, int blockX, int blockY, int blockSize) { 192 | Runtime &rt = runtimes_[threadId]; 193 | float fragCoord[2]; 194 | float fragColor[4]; 195 | 196 | int blockStartX = blockX * blockSize; 197 | int blockStartY = blockY * blockSize; 198 | 199 | for (size_t y = blockStartY; y < blockStartY + blockSize && y < colorBuffer_->height; y++) { 200 | uint8_t *rowPtr = PIXEL_ROW_PTR(y); 201 | for (size_t x = blockStartX; x < blockStartX + blockSize && x < colorBuffer_->width; x++) { 202 | fragCoord[0] = (float) x; 203 | fragCoord[1] = (float) y; 204 | 205 | rt.writeInput(fragCoord, 0); 206 | rt.execEntryPoint(); 207 | rt.readOutput(fragColor, 0); 208 | 209 | pixelColorCvt(rowPtr, x, colorBuffer_->width, fragColor); 210 | } 211 | } 212 | } 213 | 214 | void Renderer::pixelColorCvt(uint8_t *rowPtr, size_t x, size_t width, float fragColor[4]) { 215 | if (rowPtr == nullptr || x >= width) { 216 | return; 217 | } 218 | 219 | uint8_t *pixel = &rowPtr[x * 4]; 220 | pixel[0] = PIXEL_CONVERT(fragColor[0]); 221 | pixel[1] = PIXEL_CONVERT(fragColor[1]); 222 | pixel[2] = PIXEL_CONVERT(fragColor[2]); 223 | pixel[3] = PIXEL_CONVERT(fragColor[3]); 224 | } 225 | 226 | void Renderer::updateSize(int width, int height) { 227 | if (colorBuffer_) { 228 | createBuffer(width, height); 229 | uniformInput_.iResolution.x = (float) colorBuffer_->width; 230 | uniformInput_.iResolution.y = (float) colorBuffer_->height; 231 | } 232 | } 233 | 234 | void Renderer::createBuffer(int width, int height) { 235 | colorBuffer_->width = width; 236 | colorBuffer_->height = height; 237 | if (colorBuffer_->data) { 238 | delete[] colorBuffer_->data; 239 | } 240 | colorBuffer_->data = new uint8_t[width * height * 4]; 241 | } 242 | 243 | void Renderer::destroyBuffer() { 244 | if (colorBuffer_) { 245 | delete[] colorBuffer_->data; 246 | delete colorBuffer_; 247 | colorBuffer_ = nullptr; 248 | } 249 | } 250 | 251 | void Renderer::destroy() { 252 | destroyBuffer(); 253 | if (module_) { 254 | delete module_; 255 | module_ = nullptr; 256 | } 257 | } 258 | 259 | std::string Renderer::readFileString(const char *path) { 260 | std::string retStr; 261 | FILE *file = fopen(path, "rb"); 262 | if (!file) { 263 | LOGE("error open file"); 264 | return retStr; 265 | } 266 | 267 | fseek(file, 0, SEEK_END); 268 | size_t fileLength = ftell(file); 269 | if (fileLength <= 0) { 270 | LOGE("error get file size"); 271 | fclose(file); 272 | return retStr; 273 | } 274 | 275 | char *buffer = new char[fileLength + 1]; 276 | rewind(file); 277 | fread(buffer, 1, fileLength, file); 278 | buffer[fileLength] = '\0'; 279 | retStr = buffer; 280 | fclose(file); 281 | delete[] buffer; 282 | 283 | return retStr; 284 | } 285 | 286 | void Renderer::updateUniformDate(vec4 &date) { 287 | std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); 288 | time_t tt = std::chrono::system_clock::to_time_t(now); 289 | tm localTime = *localtime(&tt); 290 | 291 | date.x = (float) (localTime.tm_year + 1900); 292 | date.y = (float) (localTime.tm_mon + 1); 293 | date.z = (float) (localTime.tm_mday); 294 | date.w = (float) (localTime.tm_hour * 3600 + localTime.tm_min * 60 + localTime.tm_sec); 295 | } 296 | 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /shadertoy/renderer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "module.h" 11 | #include "runtime.h" 12 | #include "image.h" 13 | #include "timer.h" 14 | #include "settings.h" 15 | #include "threadpool.h" 16 | 17 | namespace SPVM { 18 | namespace ShaderToy { 19 | 20 | typedef struct Buffer2D_ { 21 | size_t width; 22 | size_t height; 23 | uint8_t *data; 24 | } Buffer2D; 25 | 26 | typedef struct vec3_ { 27 | float x; 28 | float y; 29 | float z; 30 | } vec3; 31 | 32 | typedef struct vec4_ { 33 | float x; 34 | float y; 35 | float z; 36 | float w; 37 | } vec4; 38 | 39 | typedef struct UniformInput_ { 40 | vec3 iResolution; // viewport resolution (in pixels) 41 | float iTime; // shader playback time (in seconds) 42 | float iTimeDelta; // render time (in seconds) 43 | int iFrame; // shader playback frame 44 | vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click 45 | vec4 iDate; // (year, month, day, time in seconds) 46 | float iSampleRate; // sound sample rate (i.e., 44100) 47 | } UniformInput; 48 | 49 | class Renderer { 50 | public: 51 | Renderer(); 52 | ~Renderer(); 53 | bool create(void *window, int width, int height); 54 | bool reloadShader(const char *shaderPath); 55 | void drawFrame(); 56 | void updateSize(int width, int height); 57 | void destroy(); 58 | 59 | inline Buffer2D *getFrame() { 60 | return colorBuffer_; 61 | } 62 | 63 | inline Settings &getSettings() { 64 | return settings_; 65 | } 66 | 67 | private: 68 | void createBuffer(int width, int height); 69 | void destroyBuffer(); 70 | 71 | static std::string readFileString(const char *path); 72 | inline void execBlockShadingSingle(int threadId, int blockX, int blockY, int blockSize); 73 | static inline void pixelColorCvt(uint8_t *rowPtr, size_t x, size_t width, float fragColor[4]); 74 | 75 | static void updateUniformDate(vec4 &date); 76 | 77 | private: 78 | Settings settings_; 79 | int rasterBlockSize_ = 32; 80 | ThreadPool threadPool_; 81 | 82 | SpvmModule *module_ = nullptr; 83 | std::vector runtimes_; 84 | 85 | std::string shaderWrapperStr_; 86 | std::vector spvBytes_; 87 | Buffer2D *colorBuffer_ = nullptr; 88 | 89 | UniformInput uniformInput_; 90 | Timer timer_; 91 | float lastFrameTime_ = 0.f; 92 | int frameIdx_ = 0; 93 | 94 | SpvmImage *iChannel0Image_ = nullptr; 95 | SpvmSampler *iChannel0Sampler_ = nullptr; 96 | SpvmSampledImage *iChannel0SampledImage_ = nullptr; 97 | }; 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /shadertoy/resourcelimits.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2016 Google, Inc. 3 | // 4 | // All rights reserved. 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions 8 | // are met: 9 | // 10 | // Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // 13 | // Redistributions in binary form must reproduce the above 14 | // copyright notice, this list of conditions and the following 15 | // disclaimer in the documentation and/or other materials provided 16 | // with the distribution. 17 | // 18 | // Neither the name of Google Inc. nor the names of its 19 | // contributors may be used to endorse or promote products derived 20 | // from this software without specific prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 | // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | // POSSIBILITY OF SUCH DAMAGE. 34 | 35 | #ifndef _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ 36 | #define _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ 37 | 38 | #include 39 | 40 | #include "glslang/Include/ResourceLimits.h" 41 | 42 | namespace glslang { 43 | 44 | // These are the default resources for TBuiltInResources, used for both 45 | // - parsing this string for the case where the user didn't supply one, 46 | // - dumping out a template for user construction of a config file. 47 | extern const TBuiltInResource DefaultTBuiltInResource; 48 | 49 | // Returns the DefaultTBuiltInResource as a human-readable string. 50 | std::string GetDefaultTBuiltInResourceString(); 51 | 52 | // Decodes the resource limits from |config| to |resources|. 53 | void DecodeResourceLimits(TBuiltInResource* resources, char* config); 54 | 55 | } // end namespace glslang 56 | 57 | #endif // _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ 58 | -------------------------------------------------------------------------------- /shadertoy/settingpanel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "settingpanel.h" 8 | #include "imgui/imgui_impl_glfw.h" 9 | #include "imgui/imgui_impl_opengl3.h" 10 | 11 | namespace SPVM { 12 | namespace ShaderToy { 13 | 14 | void SettingPanel::init(void *window, int width, int height) { 15 | frameWidth_ = width; 16 | frameHeight_ = height; 17 | 18 | // Setup Dear ImGui context 19 | IMGUI_CHECKVERSION(); 20 | ImGui::CreateContext(); 21 | ImGuiIO &io = ImGui::GetIO(); 22 | io.IniFilename = nullptr; 23 | 24 | // Setup Dear ImGui style 25 | ImGui::StyleColorsDark(); 26 | ImGuiStyle *style = &ImGui::GetStyle(); 27 | style->Alpha = 0.8f; 28 | 29 | // Setup Platform/Renderer backends 30 | ImGui_ImplGlfw_InitForOpenGL((GLFWwindow *) window, true); 31 | ImGui_ImplOpenGL3_Init("#version 150"); 32 | } 33 | 34 | void SettingPanel::onDraw() { 35 | if (!visible) { 36 | return; 37 | } 38 | 39 | // Start the Dear ImGui frame 40 | ImGui_ImplOpenGL3_NewFrame(); 41 | ImGui_ImplGlfw_NewFrame(); 42 | ImGui::NewFrame(); 43 | 44 | // Settings window 45 | ImGui::Begin("Settings", nullptr, 46 | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize 47 | | ImGuiWindowFlags_AlwaysAutoResize); 48 | drawSettings(); 49 | ImGui::SetWindowPos(ImVec2(frameWidth_ - ImGui::GetWindowWidth(), 0)); 50 | ImGui::End(); 51 | 52 | ImGui::Render(); 53 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 54 | } 55 | 56 | void SettingPanel::drawSettings() { 57 | // fps 58 | ImGui::Separator(); 59 | ImGui::Text("fps: %.01f", ImGui::GetIO().Framerate); 60 | ImGui::Text("frame: %.02f ms", 1000.0f / ImGui::GetIO().Framerate); 61 | 62 | // shader 63 | ImGui::Separator(); 64 | ImGui::Text("load shader"); 65 | for (const auto &kv : settings_.shaderPaths_) { 66 | if (ImGui::RadioButton(kv.first.c_str(), settings_.shaderPathKey_ == kv.first)) { 67 | settings_.shaderPathKey_ = kv.first; 68 | if (settings_.loadShaderCallback_) { 69 | settings_.loadShaderCallback_(settings_.getShaderPath()); 70 | } 71 | } 72 | } 73 | } 74 | 75 | void SettingPanel::destroy() { 76 | ImGui_ImplOpenGL3_Shutdown(); 77 | ImGui_ImplGlfw_Shutdown(); 78 | ImGui::DestroyContext(); 79 | } 80 | 81 | void SettingPanel::updateSize(int width, int height) { 82 | frameWidth_ = width; 83 | frameHeight_ = height; 84 | } 85 | 86 | void SettingPanel::toggleShowState() { 87 | visible = !visible; 88 | } 89 | 90 | bool SettingPanel::wantCaptureKeyboard() { 91 | ImGuiIO &io = ImGui::GetIO(); 92 | return io.WantCaptureKeyboard; 93 | } 94 | 95 | bool SettingPanel::wantCaptureMouse() { 96 | ImGuiIO &io = ImGui::GetIO(); 97 | return io.WantCaptureMouse; 98 | } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /shadertoy/settingpanel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "imgui/imgui.h" 10 | #include "settings.h" 11 | 12 | namespace SPVM { 13 | namespace ShaderToy { 14 | 15 | class SettingPanel { 16 | public: 17 | explicit SettingPanel(Settings &settings) : settings_(settings) {} 18 | 19 | void init(void *window, int width, int height); 20 | void onDraw(); 21 | void destroy(); 22 | void updateSize(int width, int height); 23 | void toggleShowState(); 24 | 25 | bool wantCaptureKeyboard(); 26 | bool wantCaptureMouse(); 27 | 28 | private: 29 | void drawSettings(); 30 | 31 | private: 32 | Settings &settings_; 33 | 34 | bool visible = true; 35 | int frameWidth_ = 0; 36 | int frameHeight_ = 0; 37 | }; 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /shadertoy/settings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace SPVM { 15 | namespace ShaderToy { 16 | 17 | const std::string ASSETS_DIR = "assets/shaders/"; 18 | const std::string SHADER_WRAPPER_PATH = "assets/wrapper.frag"; 19 | 20 | class Settings { 21 | public: 22 | Settings() { 23 | shaderPaths_["basic"] = ASSETS_DIR + "basic.frag"; 24 | shaderPaths_["fstyD4"] = ASSETS_DIR + "fstyD4.frag"; 25 | shaderPaths_["ftdfWn"] = ASSETS_DIR + "ftdfWn.frag"; 26 | shaderPaths_["Nt3BzM"] = ASSETS_DIR + "Nt3BzM.frag"; 27 | shaderPaths_["XdlSDs"] = ASSETS_DIR + "XdlSDs.frag"; 28 | 29 | shaderPathKey_ = shaderPaths_.begin()->first; 30 | } 31 | 32 | inline std::string getShaderPath() { 33 | auto it = shaderPaths_.find(shaderPathKey_); 34 | return it == shaderPaths_.end() ? "" : it->second; 35 | } 36 | 37 | public: 38 | std::string shaderWrapperPath_ = SHADER_WRAPPER_PATH; 39 | std::function loadShaderCallback_; 40 | 41 | std::string shaderPathKey_; 42 | std::unordered_map shaderPaths_; 43 | }; 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /shadertoy/third_party/glfw/lib-macos-universal/libglfw.3.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/third_party/glfw/lib-macos-universal/libglfw.3.dylib -------------------------------------------------------------------------------- /shadertoy/third_party/glfw/lib-macos-universal/libglfw3.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/third_party/glfw/lib-macos-universal/libglfw3.a -------------------------------------------------------------------------------- /shadertoy/third_party/glfw/lib-mingw-w64/glfw3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/third_party/glfw/lib-mingw-w64/glfw3.dll -------------------------------------------------------------------------------- /shadertoy/third_party/glfw/lib-mingw-w64/libglfw3.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/third_party/glfw/lib-mingw-w64/libglfw3.a -------------------------------------------------------------------------------- /shadertoy/third_party/glfw/lib-mingw-w64/libglfw3dll.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/third_party/glfw/lib-mingw-w64/libglfw3dll.a -------------------------------------------------------------------------------- /shadertoy/third_party/glfw/lib-vc2022/glfw3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/third_party/glfw/lib-vc2022/glfw3.dll -------------------------------------------------------------------------------- /shadertoy/third_party/glfw/lib-vc2022/glfw3.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/third_party/glfw/lib-vc2022/glfw3.lib -------------------------------------------------------------------------------- /shadertoy/third_party/glfw/lib-vc2022/glfw3_mt.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/third_party/glfw/lib-vc2022/glfw3_mt.lib -------------------------------------------------------------------------------- /shadertoy/third_party/glfw/lib-vc2022/glfw3dll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/shadertoy/third_party/glfw/lib-vc2022/glfw3dll.lib -------------------------------------------------------------------------------- /shadertoy/third_party/imgui/imgui/imgui_impl_glfw.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for GLFW 2 | // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) 3 | // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | 5 | // Implemented features: 6 | // [X] Platform: Clipboard support. 7 | // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] 8 | // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). 10 | 11 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 12 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 13 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 14 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 15 | 16 | // About GLSL version: 17 | // The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. 18 | // Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure! 19 | 20 | #pragma once 21 | #include "imgui.h" // IMGUI_IMPL_API 22 | 23 | struct GLFWwindow; 24 | struct GLFWmonitor; 25 | 26 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); 27 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); 28 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); 29 | IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); 30 | IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); 31 | 32 | // GLFW callbacks 33 | // - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any. 34 | // - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call those function yourself from your own GLFW callbacks. 35 | IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 36 | IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 37 | IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 38 | IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); 39 | IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); 40 | IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); 41 | IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); 42 | IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); 43 | -------------------------------------------------------------------------------- /shadertoy/third_party/imgui/imgui/imgui_impl_opengl3.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline 2 | // - Desktop GL: 2.x 3.x 4.x 3 | // - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) 4 | // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) 5 | 6 | // Implemented features: 7 | // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! 8 | // [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices. 9 | 10 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 11 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 12 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 13 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 14 | 15 | // About GLSL version: 16 | // The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. 17 | // On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" 18 | // Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. 19 | 20 | #pragma once 21 | #include "imgui.h" // IMGUI_IMPL_API 22 | 23 | // Backend API 24 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); 25 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); 26 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); 27 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); 28 | 29 | // (Optional) Called by Init/NewFrame/Shutdown 30 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); 31 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); 32 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); 33 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); 34 | 35 | // Specific OpenGL ES versions 36 | //#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten 37 | //#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android 38 | 39 | // You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. 40 | #if !defined(IMGUI_IMPL_OPENGL_ES2) \ 41 | && !defined(IMGUI_IMPL_OPENGL_ES3) 42 | 43 | // Try to detect GLES on matching platforms 44 | #if defined(__APPLE__) 45 | #include 46 | #endif 47 | #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) 48 | #define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" 49 | #elif defined(__EMSCRIPTEN__) 50 | #define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" 51 | #else 52 | // Otherwise imgui_impl_opengl3_loader.h will be used. 53 | #endif 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /shadertoy/threadpool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace SPVM { 18 | namespace ShaderToy { 19 | 20 | class ThreadPool { 21 | public: 22 | 23 | explicit ThreadPool(const size_t threadCnt = std::thread::hardware_concurrency()) 24 | : threadCnt_(threadCnt), 25 | threads_(new std::thread[threadCnt_]) { 26 | createThreads(); 27 | } 28 | 29 | ~ThreadPool() { 30 | waitTasksFinish(); 31 | running_ = false; 32 | joinThreads(); 33 | } 34 | 35 | size_t getThreadCnt() const { 36 | return threadCnt_; 37 | } 38 | 39 | size_t getTasksCnt() const { 40 | return tasksCnt_; 41 | } 42 | 43 | template 44 | void pushTask(const F &task) { 45 | tasksCnt_++; 46 | { 47 | const std::lock_guard lock(mutex_); 48 | tasks_.push(std::function(task)); 49 | } 50 | } 51 | 52 | template 53 | void pushTask(const F &task, const A &...args) { 54 | pushTask([task, args...] { task(args...); }); 55 | } 56 | 57 | void waitTasksFinish() const { 58 | while (true) { 59 | if (!paused) { 60 | if (tasksCnt_ == 0) 61 | break; 62 | } else { 63 | if (tasksRunningCnt() == 0) 64 | break; 65 | } 66 | std::this_thread::yield(); 67 | } 68 | } 69 | 70 | std::atomic paused{false}; 71 | 72 | private: 73 | void createThreads() { 74 | for (size_t i = 0; i < threadCnt_; i++) { 75 | threads_[i] = std::thread(&ThreadPool::taskWorker, this, i); 76 | } 77 | } 78 | 79 | void joinThreads() { 80 | for (size_t i = 0; i < threadCnt_; i++) { 81 | threads_[i].join(); 82 | } 83 | } 84 | 85 | size_t tasksQueuedCnt() const { 86 | const std::lock_guard lock(mutex_); 87 | return tasks_.size(); 88 | } 89 | 90 | size_t tasksRunningCnt() const { 91 | return tasksCnt_ - tasksQueuedCnt(); 92 | } 93 | 94 | bool popTask(std::function &task) { 95 | const std::lock_guard lock(mutex_); 96 | if (tasks_.empty()) 97 | return false; 98 | else { 99 | task = std::move(tasks_.front()); 100 | tasks_.pop(); 101 | return true; 102 | } 103 | } 104 | 105 | void taskWorker(size_t threadId) { 106 | while (running_) { 107 | std::function task; 108 | if (!paused && popTask(task)) { 109 | task(threadId); 110 | tasksCnt_--; 111 | } else { 112 | std::this_thread::yield(); 113 | } 114 | } 115 | } 116 | 117 | private: 118 | mutable std::mutex mutex_ = {}; 119 | std::atomic running_{true}; 120 | 121 | size_t threadCnt_; 122 | std::unique_ptr threads_; 123 | 124 | std::queue> tasks_ = {}; 125 | std::atomic tasksCnt_{0}; 126 | }; 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /shadertoy/timer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "timer.h" 8 | 9 | namespace SPVM { 10 | namespace ShaderToy { 11 | 12 | void Timer::start() { 13 | start_ = std::chrono::steady_clock::now(); 14 | } 15 | 16 | int64_t Timer::elapse() const { 17 | auto end = std::chrono::steady_clock::now(); 18 | auto duration = std::chrono::duration_cast(end - start_); 19 | return duration.count(); 20 | } 21 | 22 | } 23 | } -------------------------------------------------------------------------------- /shadertoy/timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm-shadertoy 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace SPVM { 13 | namespace ShaderToy { 14 | 15 | class Timer { 16 | public: 17 | Timer() = default; 18 | 19 | ~Timer() = default; 20 | 21 | void start(); 22 | 23 | int64_t elapse() const; 24 | 25 | private: 26 | std::chrono::time_point start_; 27 | }; 28 | 29 | class ScopedTimer { 30 | public: 31 | 32 | explicit ScopedTimer(const char *str) 33 | : tag_(str) { 34 | timer_.start(); 35 | } 36 | 37 | ~ScopedTimer() { 38 | printf("%s: %lld ms\n", tag_.c_str(), timer_.elapse()); 39 | } 40 | 41 | explicit operator bool() { 42 | return true; 43 | } 44 | 45 | private: 46 | Timer timer_; 47 | std::string tag_; 48 | }; 49 | 50 | #ifndef NDEBUG 51 | # define TIMED(X) if(SoftGL::ScopedTimer _ScopedTimer = (X)) 52 | #else 53 | # define TIMED(X) 54 | #endif 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3) 2 | project(spvm_lib) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") 5 | 6 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 7 | add_definitions(-DDEBUG) 8 | endif () 9 | 10 | aux_source_directory(. SPVM_SRCS) 11 | add_library(${PROJECT_NAME} ${SPVM_SRCS}) 12 | 13 | target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}) 14 | -------------------------------------------------------------------------------- /src/decoder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "decoder.h" 8 | #include 9 | 10 | #include "spvm.h" 11 | #include "logger.h" 12 | 13 | namespace SPVM { 14 | 15 | typedef enum { 16 | I32_ENDIAN_UNKNOWN, 17 | I32_ENDIAN_LITTLE = 0x03020100ul, 18 | I32_ENDIAN_BIG = 0x00010203ul, 19 | } I32_ENDIAN; 20 | 21 | static const union { 22 | unsigned char bytes[4]; 23 | SpvmU32 value; 24 | } o32_host_order = {{0, 1, 2, 3}}; 25 | 26 | #define I32_ENDIAN_HOST (o32_host_order.value) 27 | 28 | bool Decoder::checkEndian(const SpvmWord *ptr) { 29 | uint8_t bytes[4]; 30 | memcpy(bytes, ptr, sizeof(SpvmU32)); 31 | 32 | I32_ENDIAN endian = I32_ENDIAN_UNKNOWN; 33 | if (0x03 == bytes[0] && 0x02 == bytes[1] && 0x23 == bytes[2] && 0x07 == bytes[3]) { 34 | endian = I32_ENDIAN_LITTLE; 35 | } 36 | 37 | if (0x07 == bytes[0] && 0x23 == bytes[1] && 0x02 == bytes[2] && 0x03 == bytes[3]) { 38 | endian = I32_ENDIAN_BIG; 39 | } 40 | 41 | return I32_ENDIAN_HOST == endian; 42 | } 43 | 44 | bool Decoder::decodeFile(const char *path, SpvmModule *module) { 45 | if (path == nullptr) { 46 | LOGE("invalid file path"); 47 | return false; 48 | } 49 | 50 | FILE *file = fopen(path, "rb"); 51 | if (!file) { 52 | LOGE("error open file"); 53 | return false; 54 | } 55 | 56 | fseek(file, 0, SEEK_END); 57 | SpvmWord fileLength = ftell(file); 58 | if (fileLength <= 0) { 59 | LOGE("error get file size"); 60 | fclose(file); 61 | return false; 62 | } 63 | 64 | module->buffer = new SpvmByte[fileLength]; 65 | rewind(file); 66 | fread(module->buffer, 1, fileLength, file); 67 | fclose(file); 68 | 69 | return decodeBytes(module->buffer, fileLength, module); 70 | } 71 | 72 | bool Decoder::decodeBytes(const SpvmByte *bytes, SpvmWord length, SpvmModule *module) { 73 | auto *ptr = (SpvmWord *) bytes; 74 | 75 | // check endian by magic number 76 | if (!checkEndian(ptr++)) { 77 | LOGE("binary endian not support"); 78 | return false; 79 | } 80 | 81 | SpvmWord version = *ptr++; 82 | module->versionMajor = (version & 0x00FF0000) >> 16; 83 | module->versionMinor = (version & 0x0000FF00) >> 8; 84 | 85 | module->generatorMagic = *ptr++; 86 | module->bound = *ptr++; 87 | module->instructionSchema = *ptr++; 88 | 89 | module->code = ptr; 90 | module->codeSize = length - 5 * sizeof(SpvmWord); 91 | 92 | module->hasDerivativeOpcodes = false; 93 | module->inited = false; 94 | 95 | // check derivative opcodes 96 | while ((ptr - module->code) * sizeof(SpvmWord) < module->codeSize) { 97 | SpvmOpcode insOp = readOpcode(ptr); 98 | if (checkDerivativeOpcodes(insOp)) { 99 | module->hasDerivativeOpcodes = true; 100 | break; 101 | } 102 | ptr += insOp.wordCount; 103 | } 104 | 105 | if (module->hasDerivativeOpcodes) { 106 | LOGE("Derivative opcodes not support"); 107 | return false; 108 | } 109 | 110 | return true; 111 | } 112 | 113 | bool Decoder::checkDerivativeOpcodes(SpvmOpcode opcode) { 114 | if (opcode.op == SpvOpDPdx || opcode.op == SpvOpDPdy || opcode.op == SpvOpFwidth 115 | || opcode.op == SpvOpDPdxFine || opcode.op == SpvOpDPdyFine || opcode.op == SpvOpFwidthFine 116 | || opcode.op == SpvOpDPdxCoarse || opcode.op == SpvOpDPdyCoarse || opcode.op == SpvOpFwidthCoarse) { 117 | return true; 118 | } 119 | return false; 120 | } 121 | 122 | } -------------------------------------------------------------------------------- /src/decoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "spvm.h" 11 | #include "module.h" 12 | 13 | namespace SPVM { 14 | 15 | class Decoder { 16 | public: 17 | static bool decodeFile(const char *path, SpvmModule *module); 18 | static bool decodeBytes(const SpvmByte *bytes, SpvmWord length, SpvmModule *module); 19 | 20 | private: 21 | static bool checkEndian(const SpvmWord *ptr); 22 | static bool checkDerivativeOpcodes(SpvmOpcode opcode); 23 | }; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/ext/GLSL.std.450.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (c) 2014-2016 The Khronos Group Inc. 3 | ** 4 | ** Permission is hereby granted, free of charge, to any person obtaining a copy 5 | ** of this software and/or associated documentation files (the "Materials"), 6 | ** to deal in the Materials without restriction, including without limitation 7 | ** the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | ** and/or sell copies of the Materials, and to permit persons to whom the 9 | ** Materials are furnished to do so, subject to the following conditions: 10 | ** 11 | ** The above copyright notice and this permission notice shall be included in 12 | ** all copies or substantial portions of the Materials. 13 | ** 14 | ** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS 15 | ** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND 16 | ** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | ** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | ** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | ** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS 24 | ** IN THE MATERIALS. 25 | */ 26 | 27 | #ifndef GLSLstd450_H 28 | #define GLSLstd450_H 29 | 30 | static const int GLSLstd450Version = 100; 31 | static const int GLSLstd450Revision = 3; 32 | 33 | enum GLSLstd450 { 34 | GLSLstd450Bad = 0, // Don't use 35 | 36 | GLSLstd450Round = 1, 37 | GLSLstd450RoundEven = 2, 38 | GLSLstd450Trunc = 3, 39 | GLSLstd450FAbs = 4, 40 | GLSLstd450SAbs = 5, 41 | GLSLstd450FSign = 6, 42 | GLSLstd450SSign = 7, 43 | GLSLstd450Floor = 8, 44 | GLSLstd450Ceil = 9, 45 | GLSLstd450Fract = 10, 46 | 47 | GLSLstd450Radians = 11, 48 | GLSLstd450Degrees = 12, 49 | GLSLstd450Sin = 13, 50 | GLSLstd450Cos = 14, 51 | GLSLstd450Tan = 15, 52 | GLSLstd450Asin = 16, 53 | GLSLstd450Acos = 17, 54 | GLSLstd450Atan = 18, 55 | GLSLstd450Sinh = 19, 56 | GLSLstd450Cosh = 20, 57 | GLSLstd450Tanh = 21, 58 | GLSLstd450Asinh = 22, 59 | GLSLstd450Acosh = 23, 60 | GLSLstd450Atanh = 24, 61 | GLSLstd450Atan2 = 25, 62 | 63 | GLSLstd450Pow = 26, 64 | GLSLstd450Exp = 27, 65 | GLSLstd450Log = 28, 66 | GLSLstd450Exp2 = 29, 67 | GLSLstd450Log2 = 30, 68 | GLSLstd450Sqrt = 31, 69 | GLSLstd450InverseSqrt = 32, 70 | 71 | GLSLstd450Determinant = 33, 72 | GLSLstd450MatrixInverse = 34, 73 | 74 | GLSLstd450Modf = 35, // second operand needs an OpVariable to write to 75 | GLSLstd450ModfStruct = 36, // no OpVariable operand 76 | GLSLstd450FMin = 37, 77 | GLSLstd450UMin = 38, 78 | GLSLstd450SMin = 39, 79 | GLSLstd450FMax = 40, 80 | GLSLstd450UMax = 41, 81 | GLSLstd450SMax = 42, 82 | GLSLstd450FClamp = 43, 83 | GLSLstd450UClamp = 44, 84 | GLSLstd450SClamp = 45, 85 | GLSLstd450FMix = 46, 86 | GLSLstd450IMix = 47, // Reserved 87 | GLSLstd450Step = 48, 88 | GLSLstd450SmoothStep = 49, 89 | 90 | GLSLstd450Fma = 50, 91 | GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to 92 | GLSLstd450FrexpStruct = 52, // no OpVariable operand 93 | GLSLstd450Ldexp = 53, 94 | 95 | GLSLstd450PackSnorm4x8 = 54, 96 | GLSLstd450PackUnorm4x8 = 55, 97 | GLSLstd450PackSnorm2x16 = 56, 98 | GLSLstd450PackUnorm2x16 = 57, 99 | GLSLstd450PackHalf2x16 = 58, 100 | GLSLstd450PackDouble2x32 = 59, 101 | GLSLstd450UnpackSnorm2x16 = 60, 102 | GLSLstd450UnpackUnorm2x16 = 61, 103 | GLSLstd450UnpackHalf2x16 = 62, 104 | GLSLstd450UnpackSnorm4x8 = 63, 105 | GLSLstd450UnpackUnorm4x8 = 64, 106 | GLSLstd450UnpackDouble2x32 = 65, 107 | 108 | GLSLstd450Length = 66, 109 | GLSLstd450Distance = 67, 110 | GLSLstd450Cross = 68, 111 | GLSLstd450Normalize = 69, 112 | GLSLstd450FaceForward = 70, 113 | GLSLstd450Reflect = 71, 114 | GLSLstd450Refract = 72, 115 | 116 | GLSLstd450FindILsb = 73, 117 | GLSLstd450FindSMsb = 74, 118 | GLSLstd450FindUMsb = 75, 119 | 120 | GLSLstd450InterpolateAtCentroid = 76, 121 | GLSLstd450InterpolateAtSample = 77, 122 | GLSLstd450InterpolateAtOffset = 78, 123 | 124 | GLSLstd450NMin = 79, 125 | GLSLstd450NMax = 80, 126 | GLSLstd450NClamp = 81, 127 | 128 | GLSLstd450Count 129 | }; 130 | 131 | #endif // #ifndef GLSLstd450_H -------------------------------------------------------------------------------- /src/image.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "spvm.h" 10 | 11 | namespace SPVM { 12 | 13 | #define kMaxSamplerLodBias 64.f 14 | #define kMaxSamplerLod 64.f 15 | 16 | typedef enum SpvmFormat_ { 17 | SPVM_FORMAT_UNDEFINED = 0, 18 | SPVM_FORMAT_R8_UNORM = 9, 19 | SPVM_FORMAT_R8G8_UNORM = 16, 20 | SPVM_FORMAT_R8G8B8_UNORM = 23, 21 | SPVM_FORMAT_R8G8B8A8_UNORM = 37, 22 | } SpvmFormat; 23 | 24 | typedef enum SpvmBorderColor_ { 25 | SPVM_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0, 26 | SPVM_BORDER_COLOR_INT_TRANSPARENT_BLACK = 1, 27 | SPVM_BORDER_COLOR_FLOAT_OPAQUE_BLACK = 2, 28 | SPVM_BORDER_COLOR_INT_OPAQUE_BLACK = 3, 29 | SPVM_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 4, 30 | SPVM_BORDER_COLOR_INT_OPAQUE_WHITE = 5, 31 | } SpvmBorderColor; 32 | 33 | typedef enum SpvmCompareOp_ { 34 | SPVM_COMPARE_OP_NEVER = 0, 35 | SPVM_COMPARE_OP_LESS = 1, 36 | SPVM_COMPARE_OP_EQUAL = 2, 37 | SPVM_COMPARE_OP_LESS_OR_EQUAL = 3, 38 | SPVM_COMPARE_OP_GREATER = 4, 39 | SPVM_COMPARE_OP_NOT_EQUAL = 5, 40 | SPVM_COMPARE_OP_GREATER_OR_EQUAL = 6, 41 | SPVM_COMPARE_OP_ALWAYS = 7, 42 | } SpvmCompareOp; 43 | 44 | typedef struct SpvmImageInfo_ { 45 | SpvDim dim; 46 | SpvmFormat format; 47 | SpvmWord width; 48 | SpvmWord height; 49 | SpvmWord depth; 50 | SpvmBool mipmaps; 51 | SpvmWord baseMipLevel; 52 | SpvmWord mipLevels; 53 | SpvmBool arrayed; 54 | SpvmWord baseArrayLayer; 55 | SpvmWord arrayLayers; 56 | SpvmWord samples; 57 | } SpvmImageInfo; 58 | 59 | typedef struct SpvmSamplerInfo_ { 60 | SpvSamplerFilterMode magFilter; 61 | SpvSamplerFilterMode minFilter; 62 | SpvSamplerFilterMode mipmapMode; 63 | SpvSamplerAddressingMode addressModeU; 64 | SpvSamplerAddressingMode addressModeV; 65 | SpvSamplerAddressingMode addressModeW; 66 | SpvmF32 mipLodBias; 67 | SpvmBool compareEnable; 68 | SpvmCompareOp compareOp; 69 | SpvmF32 minLod; 70 | SpvmF32 maxLod; 71 | SpvmBorderColor borderColor; 72 | SpvmBool unnormalizedCoordinates; 73 | } SpvmSamplerInfo; 74 | 75 | typedef struct SpvmSampler_ { 76 | SpvmSamplerInfo info; 77 | } SpvmSampler; 78 | 79 | typedef struct SpvmImageLevel_ { 80 | SpvmWord level; 81 | SpvmWord width; 82 | SpvmWord height; 83 | SpvmWord depth; 84 | SpvmWord dataSize; 85 | SpvmByte *data; 86 | } SpvmImageLevel; 87 | 88 | typedef struct SpvmImageLayer_ { 89 | SpvmWord layer; 90 | SpvmWord width; 91 | SpvmWord height; 92 | SpvmWord depth; 93 | SpvmWord levelCount; 94 | SpvmImageLevel *levels; 95 | } SpvmImageLayer; 96 | 97 | typedef struct SpvmImage_ { 98 | SpvmImageInfo info; 99 | SpvmWord layerCount; 100 | SpvmImageLayer *layers; 101 | } SpvmImage; 102 | 103 | typedef struct SpvmSampledImage_ { 104 | SpvmImage *image; 105 | SpvmSampler *sampler; 106 | } SpvmSampledImage; 107 | 108 | SpvmImage *createImage(SpvmImageInfo *imageInfo); 109 | SpvmSampler *createSampler(SpvmSamplerInfo *samplerInfo = nullptr); 110 | SpvmSampler *createSamplerConstant(SpvmSamplerAttribute *attribute); 111 | SpvmSampledImage *createSampledImage(SpvmImage *image, SpvmSampler *sampler); 112 | 113 | bool uploadImageData(SpvmImage *image, SpvmByte *dataPtr, SpvmWord dataSize, 114 | SpvmWord width, SpvmWord height, SpvmWord depth = 1, 115 | SpvmWord mipLevel = 0, SpvmWord arrayLayer = 0); 116 | 117 | void generateMipmaps(SpvmImage *image, SpvSamplerFilterMode filterMode); 118 | void generateMipmaps(SpvmSampledImage *sampledImage); 119 | 120 | void destroyImage(SpvmImage *image); 121 | void destroySampler(SpvmSampler *sampler); 122 | void destroySampledImager(SpvmSampledImage *sampledImage); 123 | 124 | bool checkImageType(SpvmImage *image, SpvmTypeImage *type); 125 | 126 | void sampleImage(void *ctx, 127 | SpvmValue *retValue, 128 | SpvmValue *sampledImageValue, 129 | SpvmValue *coordinateValue, 130 | SpvmWord coordinateId, 131 | SpvmImageOperands *operands, 132 | SpvmValue *depthCompValue = nullptr, 133 | bool proj = false); 134 | 135 | void fetchImage(void *ctx, 136 | SpvmValue *retValue, 137 | SpvmValue *imageValue, 138 | SpvmValue *coordinateValue, 139 | SpvmWord coordinateId, 140 | SpvmImageOperands *operands); 141 | 142 | void queryImageFormat(void *ctx, SpvmValue *retValue, SpvmValue *imageValue); 143 | void queryImageOrder(void *ctx, SpvmValue *retValue, SpvmValue *imageValue); 144 | void queryImageSizeLod(void *ctx, SpvmValue *retValue, SpvmValue *imageValue, SpvmValue *lodValue); 145 | void queryImageSize(void *ctx, SpvmValue *retValue, SpvmValue *imageValue); 146 | void queryImageLod(void *ctx, 147 | SpvmValue *retValue, 148 | SpvmValue *sampledImageValue, 149 | SpvmValue *coordinateValue, 150 | SpvmWord coordinateId); 151 | void queryImageLevels(void *ctx, SpvmValue *retValue, SpvmValue *imageValue); 152 | void queryImageSamples(void *ctx, SpvmValue *retValue, SpvmValue *imageValue); 153 | 154 | } -------------------------------------------------------------------------------- /src/interface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include "spvm.h" 13 | 14 | namespace SPVM { 15 | 16 | struct RuntimeContext; 17 | 18 | typedef struct SpvmUniformBinding_ { 19 | SpvmWord set; 20 | SpvmWord binding; 21 | 22 | bool operator==(const struct SpvmUniformBinding_ &o) const { 23 | return o.set == set && o.binding == binding; 24 | } 25 | } SpvmUniformBinding; 26 | 27 | struct SpvmUniformBindingHash { 28 | SpvmWord operator()(const SpvmUniformBinding &o) const { 29 | return (std::hash()(o.set)) ^ (std::hash()(o.binding)); 30 | } 31 | }; 32 | 33 | class Interface { 34 | public: 35 | void init(RuntimeContext *ctx); 36 | 37 | SpvmWord getLocationByName(const char *name); 38 | SpvmUniformBinding getBindingByName(const char *name); 39 | 40 | void writeInput(void *data, SpvmWord location); 41 | void writeInputBuiltIn(void *data, SpvBuiltIn builtIn); 42 | void writeUniformBinding(void *data, SpvmWord length, SpvmWord binding, SpvmWord set = 0); 43 | void writeUniformPushConstants(void *data, SpvmWord length); 44 | 45 | void readOutput(void *data, SpvmWord location); 46 | void readOutputBuiltIn(void *data, SpvBuiltIn builtIn); 47 | 48 | private: 49 | void checkDecorations(SpvmPointer *pointer, SpvmWord pointerId, std::vector &decorations); 50 | void *writeValue(SpvmValue *value, void *data, SpvmWord length); 51 | void *readValue(SpvmValue *value, void *data); 52 | 53 | private: 54 | RuntimeContext *runtimeCtx_; 55 | 56 | std::unordered_map inputLocation_; // 57 | std::unordered_map outputLocation_; // 58 | std::unordered_map inoutBuiltIn_; // 59 | std::unordered_map bindings_; // 60 | SpvmValue *pushConstants_ = nullptr; 61 | 62 | std::unordered_map locationMap_; // 63 | std::unordered_map bindingMap_; // 64 | }; 65 | 66 | } -------------------------------------------------------------------------------- /src/logger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "logger.h" 8 | 9 | namespace SPVM { 10 | 11 | void *Logger::logContext_ = nullptr; 12 | LogFunc Logger::logFunc_ = nullptr; 13 | 14 | #if DEBUG 15 | LogLevel Logger::minLevel_ = LOG_INFO; 16 | #else 17 | LogLevel Logger::minLevel_ = LOG_WARNING; 18 | #endif 19 | 20 | char Logger::buf_[MAX_LOG_LENGTH] = {}; 21 | 22 | void Logger::setLogFunc(void *ctx, LogFunc func) { 23 | logContext_ = ctx; 24 | logFunc_ = func; 25 | } 26 | 27 | void Logger::setLogLevel(LogLevel level) { 28 | minLevel_ = level; 29 | } 30 | 31 | void Logger::log(LogLevel level, const char *file, int line, const char *message, ...) { 32 | if (level < minLevel_) { 33 | return; 34 | } 35 | 36 | va_list argPtr; 37 | va_start(argPtr, message); 38 | vsnprintf(buf_, MAX_LOG_LENGTH - 1, message, argPtr); 39 | va_end(argPtr); 40 | buf_[MAX_LOG_LENGTH - 1] = '\0'; 41 | 42 | if (logFunc_ != nullptr) { 43 | logFunc_(logContext_, level, buf_); 44 | return; 45 | } 46 | 47 | switch (level) { 48 | #ifdef LOG_SOURCE_LINE 49 | case LOG_INFO: fprintf(stdout, "[SPVM][INFO] %s:%d: %s\n", file, line, buf_); break; 50 | case LOG_DEBUG: fprintf(stdout, "[SPVM][DEBUG] %s:%d: %s\n", file, line, buf_); break; 51 | case LOG_WARNING: fprintf(stdout, "[SPVM][WARNING] %s:%d: %s\n", file, line, buf_); break; 52 | case LOG_ERROR: fprintf(stdout, "[SPVM][ERROR] %s:%d: %s\n", file, line, buf_); break; 53 | #else 54 | case LOG_INFO: fprintf(stdout, "[SPVM][INFO] : %s\n", buf_); break; 55 | case LOG_DEBUG: fprintf(stdout, "[SPVM][DEBUG] : %s\n", buf_); break; 56 | case LOG_WARNING: fprintf(stdout, "[SPVM][WARNING] : %s\n", buf_); break; 57 | case LOG_ERROR: fprintf(stdout, "[SPVM][ERROR] : %s\n", buf_); break; 58 | #endif 59 | } 60 | fflush(stdout); 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /src/logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace SPVM { 14 | 15 | #define LOG_SOURCE_LINE 16 | 17 | #define SPVM_FILENAME (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__) 18 | 19 | #define LOGI(...) SPVM::Logger::log(SPVM::LOG_INFO, SPVM_FILENAME, __LINE__, __VA_ARGS__) 20 | #define LOGD(...) SPVM::Logger::log(SPVM::LOG_DEBUG, SPVM_FILENAME, __LINE__, __VA_ARGS__) 21 | #define LOGW(...) SPVM::Logger::log(SPVM::LOG_WARNING, SPVM_FILENAME, __LINE__, __VA_ARGS__) 22 | #define LOGE(...) SPVM::Logger::log(SPVM::LOG_ERROR, SPVM_FILENAME, __LINE__, __VA_ARGS__) 23 | 24 | static constexpr int MAX_LOG_LENGTH = 1024; 25 | 26 | typedef void (*LogFunc)(void *context, int level, const char *msg); 27 | 28 | enum LogLevel { 29 | LOG_INFO, 30 | LOG_DEBUG, 31 | LOG_WARNING, 32 | LOG_ERROR, 33 | }; 34 | 35 | class Logger { 36 | public: 37 | static void setLogFunc(void *ctx, LogFunc func); 38 | static void setLogLevel(LogLevel level); 39 | static void log(LogLevel level, const char *file, int line, const char *message, ...); 40 | 41 | private: 42 | static void *logContext_; 43 | static LogFunc logFunc_; 44 | static LogLevel minLevel_; 45 | 46 | static char buf_[MAX_LOG_LENGTH]; 47 | }; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/module.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "module.h" 8 | 9 | namespace SPVM { 10 | 11 | SpvmModule::SpvmModule() : buffer(nullptr) {} 12 | 13 | SpvmModule::~SpvmModule() { 14 | if (buffer) { 15 | delete[] buffer; 16 | buffer = nullptr; 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/module.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "spvm.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace SPVM { 15 | 16 | class SpvmModule { 17 | public: 18 | SPVM::SpvmByte *buffer; 19 | 20 | SpvmByte versionMajor; 21 | SpvmByte versionMinor; 22 | SpvmWord generatorMagic; 23 | SpvmWord bound; 24 | SpvmWord instructionSchema; 25 | 26 | SpvmSource source; 27 | std::vector sourceExtensions; 28 | std::vector names; 29 | 30 | SpvmWord *code; 31 | SpvmWord codeSize; // bytes 32 | 33 | std::vector capabilities; 34 | std::vector extensions; 35 | SpvAddressingModel addressingModel; 36 | SpvMemoryModel memoryModel; 37 | std::vector entryPoints; 38 | std::vector executionModes; 39 | 40 | std::unordered_map> decorations; 41 | std::vector globalPointers; 42 | 43 | // whether the module contains derivative opcodes(OpDPdx, OpDPdy, ...) 44 | bool hasDerivativeOpcodes; 45 | bool inited; 46 | public: 47 | SpvmModule(); 48 | ~SpvmModule(); 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/runtime.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "runtime.h" 8 | #include "opcodes.inc" 9 | 10 | namespace SPVM { 11 | 12 | Runtime::Runtime() : heapSize_(0), heap_(nullptr) {} 13 | 14 | Runtime::~Runtime() { 15 | if (heap_) { 16 | delete[] heap_; 17 | heap_ = nullptr; 18 | } 19 | } 20 | 21 | bool Runtime::initWithModule(SpvmModule *module, SpvmWord heapSize, SpvmByte *heap) { 22 | if (heapSize_ != 0) { 23 | LOGE("initWithModule already inited"); 24 | return false; 25 | } 26 | 27 | if (heapSize <= 0) { 28 | LOGE("initWithModule error heapSize: %d", heapSize); 29 | return false; 30 | } 31 | 32 | heapSize_ = heapSize; 33 | SpvmByte *heapBase = heap; 34 | if (heapBase == nullptr) { 35 | heap_ = new SpvmByte[heapSize_]; 36 | memset(heap_, 0, heapSize_ * sizeof(SpvmByte)); 37 | heapBase = heap_; 38 | } else { 39 | heap_ = nullptr; 40 | } 41 | 42 | ctx_.module = module; 43 | ctx_.resultsInit = (void **) heapBase; 44 | ctx_.results = (void **) (heapBase + module->bound * sizeof(void *)); 45 | ctx_.currFrame = nullptr; 46 | 47 | ctx_.pc = ctx_.module->code; 48 | ctx_.sp = (SpvmByte *) ctx_.results + module->bound * sizeof(void *); 49 | 50 | execRet_ = Result_NoError; 51 | bool success = execContinue(); 52 | if (!success) { 53 | LOGE("initWithModule failed"); 54 | return false; 55 | } 56 | 57 | // store init results status 58 | memcpy(ctx_.resultsInit, ctx_.results, module->bound * sizeof(void *)); 59 | 60 | module->inited = true; 61 | interface_.init(&ctx_); 62 | return true; 63 | } 64 | 65 | bool Runtime::execEntryPoint(SpvmWord entryIdx) { 66 | bool success = execPrepare(entryIdx); 67 | if (!success) { 68 | LOGE("prepare exec failed"); 69 | return false; 70 | } 71 | 72 | return execContinue(); 73 | } 74 | 75 | bool Runtime::execPrepare(SpvmWord entryIdx) { 76 | if (ctx_.module->entryPoints.empty()) { 77 | LOGE("execPrepare error: no entry point defined"); 78 | return false; 79 | } 80 | 81 | // load init results status 82 | memcpy(ctx_.results, ctx_.resultsInit, ctx_.module->bound * sizeof(void *)); 83 | 84 | auto &entry = ctx_.module->entryPoints[entryIdx]; 85 | auto *func = (SpvmFunction *) ctx_.results[entry.id]; 86 | ctx_.sp = ctx_.stackBase; 87 | ctx_.pc = func->code; 88 | 89 | SpvmWord *pc = ctx_.pc; 90 | SpvmByte *sp = ctx_.sp; 91 | 92 | // create frame 93 | ctx_.currFrame = (SpvmFrame *) HEAP_MALLOC(sizeof(SpvmFrame)); 94 | ctx_.currFrame->prevFrame = nullptr; 95 | ctx_.currFrame->paramsIdx = 0; 96 | ctx_.currFrame->params = nullptr; 97 | ctx_.currFrame->resultValue = nullptr; 98 | ctx_.currFrame->parentBlockId = 0; 99 | ctx_.currFrame->currBlockId = 0; 100 | ctx_.currFrame->pc = pc; 101 | 102 | // write back 103 | ctx_.pc = pc; 104 | ctx_.sp = sp; 105 | 106 | execRet_ = Result_NoError; 107 | return true; 108 | } 109 | 110 | bool Runtime::execContinue() { 111 | if (execRet_ == Result_ExecEnd) { 112 | return true; 113 | } 114 | RuntimeContext *ctx = &ctx_; 115 | 116 | SpvmWord *pc = ctx->pc; 117 | SpvmByte *sp = ctx->sp; 118 | SpvmOpcode opcode = READ_OPCODE(); 119 | execRet_ = execOpCodes(pc, sp, opcode, ctx); 120 | 121 | return execRet_ != Result_Error; 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/runtime.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "spvm.h" 10 | #include "module.h" 11 | #include "interface.h" 12 | 13 | namespace SPVM { 14 | 15 | typedef struct SpvmFrame { 16 | struct SpvmFrame *prevFrame; 17 | SpvmWord paramsIdx; 18 | void **params; 19 | SpvmValue *resultValue; 20 | SpvmWord parentBlockId; 21 | SpvmWord currBlockId; 22 | SpvmWord *pc; 23 | } SpvmFrame; 24 | 25 | class Runtime; 26 | 27 | typedef enum { 28 | Result_NoError = 0, 29 | Result_Unreachable, 30 | Result_Killed, 31 | Result_Error, 32 | Result_InitEnd, 33 | Result_ExecEnd, 34 | } RuntimeResult; 35 | 36 | typedef struct RuntimeContext { 37 | SpvmModule *module; 38 | void **resultsInit; 39 | void **results; 40 | SpvmByte *stackBase; 41 | SpvmFrame *currFrame; 42 | SpvmWord *pc; 43 | SpvmByte *sp; 44 | } RuntimeContext; 45 | 46 | class Runtime { 47 | public: 48 | Runtime(); 49 | ~Runtime(); 50 | bool initWithModule(SpvmModule *module, SpvmWord heapSize, SpvmByte *heap = nullptr); 51 | bool execEntryPoint(SpvmWord entryIdx = 0); 52 | 53 | bool execPrepare(SpvmWord entryIdx = 0); 54 | bool execContinue(); 55 | 56 | inline SpvmWord getLocationByName(const char *name) { 57 | return interface_.getLocationByName(name); 58 | } 59 | 60 | inline SpvmUniformBinding getBindingByName(const char *name) { 61 | return interface_.getBindingByName(name); 62 | } 63 | 64 | inline void writeInput(void *data, SpvmWord location) { 65 | interface_.writeInput(data, location); 66 | } 67 | inline void writeInputBuiltIn(void *data, SpvBuiltIn builtIn) { 68 | interface_.writeInputBuiltIn(data, builtIn); 69 | } 70 | inline void writeUniformBinding(void *data, SpvmWord length, SpvmWord binding, SpvmWord set = 0) { 71 | interface_.writeUniformBinding(data, length, binding, set); 72 | } 73 | inline void writeUniformPushConstants(void *data, SpvmWord length) { 74 | interface_.writeUniformPushConstants(data, length); 75 | } 76 | inline void readOutput(void *data, SpvmWord location) { 77 | interface_.readOutput(data, location); 78 | } 79 | inline void readOutputBuiltIn(void *data, SpvBuiltIn builtIn) { 80 | interface_.readOutputBuiltIn(data, builtIn); 81 | } 82 | 83 | inline RuntimeContext &getRuntimeContext() { 84 | return ctx_; 85 | } 86 | 87 | private: 88 | RuntimeContext ctx_; 89 | Interface interface_; 90 | RuntimeResult execRet_; 91 | SpvmWord heapSize_; 92 | SpvmByte *heap_; // [resultIds-init][resultIds][types, global vars][frame0, frame0 vars][frame1, frame1 vars] ... 93 | }; 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/spvm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "spvm.h" 8 | #include "opstrings.h" 9 | #include "logger.h" 10 | 11 | namespace SPVM { 12 | 13 | #ifndef HEAP_MALLOC 14 | #define HEAP_MALLOC(n) sp; sp += (n) 15 | #endif 16 | 17 | SpvmOpcode readOpcode(const SpvmWord *pc) { 18 | SpvmWord word = *pc; 19 | 20 | SpvmOpcode opcode; 21 | opcode.op = SpvOp(word & SpvOpCodeMask); 22 | opcode.wordCount = (word & (~SpvOpCodeMask)) >> SpvWordCountShift; 23 | 24 | // LOGD("read opcode: %s, size: %d", spvmOpString(opcode.op), opcode.wordCount); 25 | return opcode; 26 | } 27 | 28 | SpvmValue *createValue(SpvmTypeBase *type, SpvmByte **psp) { 29 | if (type->type == SpvOpTypeVoid) { 30 | return nullptr; 31 | } 32 | SpvmByte *sp = *psp; 33 | SpvmValue *ret = (SpvmValue *) HEAP_MALLOC(sizeof(SpvmValue)); 34 | ret->type = type; 35 | ret->memberCnt = type->memberCnt; 36 | switch (type->type) { 37 | case SpvOpTypeBool: 38 | case SpvOpTypeInt: 39 | case SpvOpTypeFloat: { 40 | ret->value.members = nullptr; 41 | break; 42 | } 43 | case SpvOpTypeVector: { 44 | SpvmTypeVector *vecType = (SpvmTypeVector *) type; 45 | ret->value.members = (SpvmValue **) HEAP_MALLOC(ret->memberCnt * sizeof(SpvmValue *)); 46 | for (SpvmWord i = 0; i < ret->memberCnt; i++) { 47 | ret->value.members[i] = createValue(vecType->componentType, &sp); 48 | } 49 | break; 50 | } 51 | case SpvOpTypeMatrix: { 52 | SpvmTypeMatrix *matType = (SpvmTypeMatrix *) type; 53 | ret->value.members = (SpvmValue **) HEAP_MALLOC(ret->memberCnt * sizeof(SpvmValue *)); 54 | for (SpvmWord i = 0; i < ret->memberCnt; i++) { 55 | ret->value.members[i] = createValue(matType->columnType, &sp); 56 | } 57 | break; 58 | } 59 | case SpvOpTypeArray: { 60 | SpvmTypeArray *arrayType = (SpvmTypeArray *) type; 61 | ret->value.members = (SpvmValue **) HEAP_MALLOC(ret->memberCnt * sizeof(SpvmValue *)); 62 | for (SpvmWord i = 0; i < ret->memberCnt; i++) { 63 | ret->value.members[i] = createValue(arrayType->elementType, &sp); 64 | } 65 | break; 66 | } 67 | case SpvOpTypeRuntimeArray: { 68 | SpvmTypeRuntimeArray *rtArrType = (SpvmTypeRuntimeArray *) type; 69 | if (rtArrType->memberCnt <= 0) { 70 | ret->value.members = nullptr; 71 | } else { 72 | ret->value.members = (SpvmValue **) HEAP_MALLOC(ret->memberCnt * sizeof(SpvmValue *)); 73 | for (SpvmWord i = 0; i < ret->memberCnt; i++) { 74 | ret->value.members[i] = createValue(rtArrType->elementType, &sp); 75 | } 76 | } 77 | break; 78 | } 79 | case SpvOpTypeStruct: { 80 | SpvmTypeStruct *structType = (SpvmTypeStruct *) type; 81 | ret->value.members = (SpvmValue **) HEAP_MALLOC(ret->memberCnt * sizeof(SpvmValue *)); 82 | for (SpvmWord i = 0; i < ret->memberCnt; i++) { 83 | ret->value.members[i] = createValue(structType->memberTypes[i], &sp); 84 | } 85 | break; 86 | } 87 | case SpvOpTypeImage: { 88 | SpvmTypeImage *imgType = (SpvmTypeImage *) type; 89 | ret->value.image = (SpvmImageInternal *) HEAP_MALLOC(sizeof(SpvmImageInternal)); 90 | ret->value.image->type = imgType; 91 | ret->value.image->opaque = nullptr; 92 | break; 93 | } 94 | case SpvOpTypeSampler: { 95 | ret->value.sampler = (SpvmSamplerInternal *) HEAP_MALLOC(sizeof(SpvmSamplerInternal)); 96 | ret->value.sampler->attributes = nullptr; 97 | ret->value.sampler->opaque = nullptr; 98 | break; 99 | } 100 | case SpvOpTypeSampledImage: { 101 | SpvmTypeSampledImage *sampledImgType = (SpvmTypeSampledImage *) type; 102 | ret->value.sampledImage = (SpvmSampledImageInternal *) HEAP_MALLOC(sizeof(SpvmSampledImageInternal)); 103 | SpvmValue *imageVal = createValue(sampledImgType->imageType, &sp); 104 | ret->value.sampledImage->image = imageVal->value.image; 105 | ret->value.sampledImage->sampler = (SpvmSamplerInternal *) HEAP_MALLOC(sizeof(SpvmSamplerInternal)); 106 | ret->value.sampledImage->sampler->attributes = nullptr; 107 | ret->value.sampledImage->sampler->opaque = nullptr; 108 | break; 109 | } 110 | default: { 111 | LOGE("createWithType not implement: %s", spvmOpString(type->type)); 112 | break; 113 | } 114 | } 115 | 116 | *psp = sp; 117 | return ret; 118 | } 119 | 120 | SpvmWord getTypeSize(SpvmTypeBase *type) { 121 | switch (type->type) { 122 | case SpvOpTypeBool: 123 | return sizeof(SpvmBool); 124 | case SpvOpTypeInt: { 125 | SpvmTypeInt *intType = (SpvmTypeInt *) type; 126 | return intType->width / 8; 127 | } 128 | case SpvOpTypeFloat: { 129 | SpvmTypeFloat *floatType = (SpvmTypeFloat *) type; 130 | return floatType->width / 8; 131 | } 132 | case SpvOpTypeRuntimeArray: { 133 | SpvmTypeRuntimeArray *rtArrType = (SpvmTypeRuntimeArray *) type; 134 | return rtArrType->memberCnt * getTypeSize(rtArrType->elementType); 135 | } 136 | case SpvOpTypeVector: { 137 | SpvmTypeVector *vecType = (SpvmTypeVector *) type; 138 | return vecType->memberCnt * getTypeSize(vecType->componentType); 139 | } 140 | case SpvOpTypeMatrix: { 141 | SpvmTypeMatrix *matType = (SpvmTypeMatrix *) type; 142 | return matType->memberCnt * getTypeSize(matType->columnType); 143 | } 144 | case SpvOpTypeArray: { 145 | SpvmTypeArray *arrType = (SpvmTypeArray *) type; 146 | return arrType->memberCnt * getTypeSize(arrType->elementType); 147 | } 148 | case SpvOpTypeStruct: { 149 | SpvmTypeStruct *structType = (SpvmTypeStruct *) type; 150 | SpvmWord totalSize = 0; 151 | for (SpvmWord i = 0; i < structType->memberCnt; i++) { 152 | totalSize += getTypeSize(structType->memberTypes[i]); 153 | } 154 | return totalSize; 155 | } 156 | default: 157 | break; 158 | } 159 | 160 | return 0; 161 | } 162 | 163 | void copyValue(SpvmValue *dst, SpvmValue *src) { 164 | if (src->memberCnt > 0) { 165 | for (SpvmWord i = 0; i < src->memberCnt; i++) { 166 | copyValue(dst->value.members[i], src->value.members[i]); 167 | } 168 | } else { 169 | dst->value = src->value; 170 | } 171 | } 172 | 173 | SpvmValue *setValueF32(SpvmValue *ret, SpvmF32 val) { 174 | if (ret->memberCnt > 0) { 175 | for (SpvmWord i = 0; i < ret->memberCnt; i++) { 176 | ret->VECTOR_f32(i) = val; 177 | } 178 | } else { 179 | ret->value.f32 = val; 180 | } 181 | 182 | return ret; 183 | } 184 | 185 | void writeToValue(SpvmValue *retValue, SpvmVec4 vec) { 186 | if (retValue->memberCnt == 0) { 187 | retValue->value.i32 = vec.elem[0].i32; 188 | } else { 189 | for (SpvmWord i = 0; i < retValue->memberCnt && i < 4; i++) { 190 | retValue->value.members[i]->value.i32 = vec.elem[i].i32; 191 | } 192 | } 193 | } 194 | 195 | SpvmVec4 readFromValue(SpvmValue *value) { 196 | SpvmVec4 retVec{0, 0, 0, 0}; 197 | if (value->memberCnt > 0) { 198 | for (SpvmWord i = 0; i < value->memberCnt && i < 3; i++) { 199 | retVec.elem[i].i32 = value->value.members[i]->value.i32; 200 | } 201 | } else { 202 | retVec.elem[0].i32 = value->value.i32; 203 | } 204 | return retVec; 205 | } 206 | 207 | void valueSubF32(SpvmValue *ret, SpvmValue *a, SpvmValue *b) { 208 | if (ret->memberCnt > 0) { 209 | for (SpvmWord i = 0; i < ret->memberCnt; i++) { 210 | ret->VECTOR_f32(i) = a->VECTOR_f32(i) - b->VECTOR_f32(i); 211 | } 212 | } else { 213 | ret->value.f32 = a->value.f32 - b->value.f32; 214 | } 215 | } 216 | 217 | } -------------------------------------------------------------------------------- /src/spvm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include "spirv.h" 12 | 13 | namespace SPVM { 14 | 15 | #define EXT_GLSLstd450 "GLSL.std.450" 16 | 17 | #define VECTOR_boolean(idx) value.members[(idx)]->value.boolean 18 | #define VECTOR_i32(idx) value.members[(idx)]->value.i32 19 | #define VECTOR_u32(idx) value.members[(idx)]->value.u32 20 | #define VECTOR_f32(idx) value.members[(idx)]->value.f32 21 | #define VECTOR_value(idx) value.members[(idx)]->value 22 | 23 | #define MATRIX_boolean(col, row) value.members[(col)]->value.members[(row)]->value.boolean 24 | #define MATRIX_i32(col, row) value.members[(col)]->value.members[(row)]->value.i32 25 | #define MATRIX_u32(col, row) value.members[(col)]->value.members[(row)]->value.u32 26 | #define MATRIX_f32(col, row) value.members[(col)]->value.members[(row)]->value.f32 27 | #define MATRIX_value(col, row) value.members[(col)]->value.members[(row)]->value 28 | 29 | typedef void SpvmVoid; 30 | typedef uint8_t SpvmByte; 31 | typedef uint32_t SpvmWord; 32 | typedef bool SpvmBool; 33 | typedef int8_t SpvmI8; 34 | typedef uint8_t SpvmU8; 35 | typedef int16_t SpvmI16; 36 | typedef uint16_t SpvmU16; 37 | typedef int32_t SpvmI32; 38 | typedef uint32_t SpvmU32; 39 | typedef int64_t SpvmI64; 40 | typedef uint64_t SpvmU64; 41 | typedef float SpvmF32; 42 | typedef double SpvmF64; 43 | 44 | #define SpvmTrue true 45 | #define SpvmFalse false 46 | #define SpvmResultIdInvalid 0 47 | #define SpvmNAN nan("") 48 | 49 | typedef struct SpvmString_ { 50 | SpvmWord wordCount; 51 | SpvmWord strLen; 52 | SpvmByte *str; 53 | } SpvmString; 54 | 55 | typedef struct SpvmOpcode_ { 56 | SpvOp op; 57 | SpvmWord wordCount; 58 | } SpvmOpcode; 59 | 60 | typedef struct SpvmDebugSource_ { 61 | SpvSourceLanguage language; 62 | SpvmWord languageVersion; 63 | } SpvmSource; 64 | 65 | typedef struct SpvmName_ { 66 | SpvmWord targetId; 67 | SpvmWord memberIdx; 68 | SpvmString name; 69 | } SpvmName; 70 | 71 | typedef struct SpvmExtension_ { 72 | SpvmString name; 73 | } SpvmExtension; 74 | 75 | typedef struct SpvmExtInstImport_ { 76 | SpvmWord setId; 77 | } SpvmExtInstImport; 78 | 79 | typedef struct SpvmEntryPoint_ { 80 | SpvExecutionModel executionModel; 81 | SpvmWord id; 82 | SpvmString name; 83 | SpvmWord globalIdCnt; 84 | SpvmWord *globalIds; 85 | } SpvmEntryPoint; 86 | 87 | typedef struct SpvmExecutionMode_ { 88 | SpvmWord entryPointId; 89 | SpvExecutionMode mode; 90 | } SpvmExecutionMode; 91 | 92 | typedef struct SpvmDecorationExtra_ { 93 | union { 94 | SpvmWord location; 95 | SpvBuiltIn builtIn; 96 | SpvmWord binding; 97 | SpvmWord descriptorSet; 98 | } value; 99 | } SpvmDecorationExtra; 100 | 101 | typedef struct SpvmDecoration_ { 102 | SpvmWord targetId; 103 | SpvmWord memberIdx; 104 | SpvDecoration decoration; 105 | SpvmDecorationExtra extra; 106 | } SpvmDecoration; 107 | 108 | typedef struct SpvmTypeBase_ { 109 | SpvOp type; 110 | SpvmWord memberCnt; 111 | SpvmWord resultId; 112 | } SpvmTypeBase; 113 | 114 | typedef struct SpvmTypeVoid_ : SpvmTypeBase { 115 | } SpvmTypeVoid; 116 | 117 | typedef struct SpvmTypeBool_ : SpvmTypeBase { 118 | } SpvmTypeBool; 119 | 120 | typedef struct SpvmTypeInt_ : SpvmTypeBase { 121 | SpvmWord width; 122 | SpvmByte signedness; 123 | } SpvmTypeInt; 124 | 125 | typedef struct SpvmTypeFloat_ : SpvmTypeBase { 126 | SpvmWord width; 127 | } SpvmTypeFloat; 128 | 129 | typedef struct SpvmTypeVector_ : SpvmTypeBase { 130 | SpvmTypeBase *componentType; 131 | } SpvmTypeVector; 132 | 133 | typedef struct SpvmTypeMatrix_ : SpvmTypeBase { 134 | SpvmTypeBase *columnType; 135 | } SpvmTypeMatrix; 136 | 137 | typedef struct SpvmTypeImage_ : SpvmTypeBase { 138 | SpvmTypeBase *sampledType; 139 | SpvDim dim; 140 | SpvmByte depth; 141 | SpvmByte arrayed; 142 | SpvmByte ms; 143 | SpvmByte sampled; 144 | SpvImageFormat format; 145 | SpvAccessQualifier accessQualifier; 146 | } SpvmTypeImage; 147 | 148 | typedef struct SpvmTypeSampler_ : SpvmTypeBase { 149 | } SpvmTypeSampler; 150 | 151 | typedef struct SpvmTypeSampledImage_ : SpvmTypeBase { 152 | SpvmTypeImage *imageType; 153 | } SpvmTypeSampledImage; 154 | 155 | typedef struct SpvmTypeArray_ : SpvmTypeBase { 156 | SpvmTypeBase *elementType; 157 | } SpvmTypeArray; 158 | 159 | typedef struct SpvmTypeRuntimeArray_ : SpvmTypeBase { 160 | SpvmTypeBase *elementType; 161 | } SpvmTypeRuntimeArray; 162 | 163 | typedef struct SpvmTypeStruct_ : SpvmTypeBase { 164 | SpvmTypeBase **memberTypes; 165 | } SpvmTypeStruct; 166 | 167 | typedef struct SpvmTypePointer_ : SpvmTypeBase { 168 | SpvStorageClass storageClass; 169 | SpvmTypeBase *objType; 170 | } SpvmTypePointer; 171 | 172 | typedef struct SpvmTypeFunction_ : SpvmTypeBase { 173 | SpvmTypeBase *returnType; 174 | SpvmWord parameterCnt; 175 | SpvmTypeBase **parameterTypes; 176 | } SpvmTypeFunction; 177 | 178 | typedef struct SpvmFunction_ { 179 | SpvmWord functionControl; 180 | SpvmTypeBase *functionType; 181 | SpvmWord *code; 182 | } SpvmFunction; 183 | 184 | typedef struct SpvmSamplerAttribute_ { 185 | SpvSamplerAddressingMode addressingMode; 186 | SpvmByte normalized; 187 | SpvSamplerFilterMode filterMode; 188 | } SpvmSamplerAttribute; 189 | 190 | typedef struct SpvmSamplerInternal_ { 191 | SpvmSamplerAttribute *attributes; 192 | SpvmVoid *opaque; 193 | } SpvmSamplerInternal; 194 | 195 | typedef struct SpvmImageInternal_ { 196 | SpvmTypeImage *type; 197 | SpvmVoid *opaque; 198 | } SpvmImageInternal; 199 | 200 | typedef struct SpvmSampledImageInternal_ { 201 | SpvmImageInternal *image; 202 | SpvmSamplerInternal *sampler; 203 | } SpvmSampledImageInternal; 204 | 205 | typedef struct SpvmValue_ { 206 | SpvmTypeBase *type; 207 | SpvmWord memberCnt; 208 | 209 | union { 210 | SpvmBool boolean; 211 | SpvmI32 i32; 212 | SpvmU32 u32; 213 | SpvmF32 f32; 214 | 215 | SpvmSamplerInternal *sampler; 216 | SpvmImageInternal *image; 217 | SpvmSampledImageInternal *sampledImage; 218 | 219 | SpvmValue_ **members; 220 | } value; 221 | } SpvmValue; 222 | 223 | typedef struct SpvmPointer_ { 224 | SpvmTypePointer *resultType; 225 | SpvmValue *objPtr; 226 | } SpvmPointer; 227 | 228 | typedef struct SpvmImageOperands_ { 229 | SpvmValue *bias; 230 | SpvmValue *lod; 231 | SpvmValue *dx, *dy; 232 | SpvmValue *offset; 233 | SpvmValue *offsets; // OpImageGather, OpImageDrefGather 234 | SpvmValue *sample; 235 | SpvmValue *minLod; 236 | } SpvmImageOperands; 237 | 238 | typedef union SpvmVecElement_ { 239 | SpvmI32 i32; 240 | SpvmU32 u32; 241 | SpvmF32 f32; 242 | } SpvmVecElement; 243 | 244 | typedef struct SpvmVec2_ { 245 | SpvmVecElement elem[2]; 246 | } SpvmVec2; 247 | 248 | typedef struct SpvmVec3_ { 249 | SpvmVecElement elem[3]; 250 | } SpvmVec3; 251 | 252 | typedef struct SpvmVec4_ { 253 | SpvmVecElement elem[4]; 254 | } SpvmVec4; 255 | 256 | SpvmOpcode readOpcode(const SpvmWord *pc); 257 | SpvmValue *createValue(SpvmTypeBase *type, SpvmByte **psp); 258 | void copyValue(SpvmValue *dst, SpvmValue *src); 259 | SpvmValue *setValueF32(SpvmValue *ret, SpvmF32 val); 260 | SpvmWord getTypeSize(SpvmTypeBase *type); 261 | void writeToValue(SpvmValue *retValue, SpvmVec4 vec); 262 | SpvmVec4 readFromValue(SpvmValue *value); 263 | 264 | void valueSubF32(SpvmValue *ret, SpvmValue *a, SpvmValue *b); 265 | 266 | } -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3) 2 | project(spvm_test) 3 | 4 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 5 | add_subdirectory(googletest) 6 | 7 | add_executable(${PROJECT_NAME} main.cpp 8 | test_core.cpp 9 | test_image.cpp 10 | test_ext.cpp 11 | ) 12 | target_link_libraries(${PROJECT_NAME} spvm_lib gtest_main) 13 | 14 | include(GoogleTest) 15 | gtest_discover_tests(${PROJECT_NAME} WORKING_DIRECTORY $) 16 | 17 | # copy assets 18 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 19 | COMMAND ${CMAKE_COMMAND} -E remove_directory $/assets 20 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets $/assets 21 | ) 22 | -------------------------------------------------------------------------------- /test/assets/arithmetic_0.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | void main() 7 | { 8 | ASSERT_BEGIN(32) 9 | 10 | // OpSNegate 11 | int a0 = 5; 12 | int outOpSNegate = -a0; 13 | ASSERT(outOpSNegate == -5); 14 | 15 | // OpFNegate 16 | float a1 = 5.f; 17 | float outOpFNegate = -a1; 18 | ASSERT(outOpFNegate == -5.f); 19 | 20 | // OpIAdd 21 | int a2 = 5; 22 | int outOpIAdd = a2 + 1; 23 | ASSERT(outOpIAdd == 6); 24 | 25 | // OpFAdd 26 | vec3 a3 = vec3(1.f, 1.f, 1.f); 27 | vec3 outOpFAdd = a3 + vec3(0.1f, 0.2f, 0.3f); 28 | ASSERT(outOpFAdd == vec3(1.1f, 1.2f, 1.3f)); 29 | 30 | // OpISub 31 | ivec3 a4 = ivec3(5, 5, 5); 32 | ivec3 outOpISub = a4 - ivec3(4, 5, 6); 33 | ASSERT(outOpISub == ivec3(1, 0, -1)); 34 | 35 | // OpFSub 36 | vec3 a5 = vec3(1.f, 1.f, 1.f); 37 | vec3 outOpFSub = a5 - vec3(0.1f, 0.2f, 0.3f); 38 | ASSERT(outOpFSub == vec3(0.9f, 0.8f, 0.7f)); 39 | 40 | // OpIMul 41 | ivec3 a6 = ivec3(1, -2, 3); 42 | ivec3 outOpIMul = a6 * ivec3(4, 5, 6); 43 | ASSERT(outOpIMul == ivec3(4, -10, 18)); 44 | 45 | // OpFMul 46 | vec3 a7 = vec3(1.f, -2.f, 1.f); 47 | vec3 outOpFMul = a7 * vec3(0.1f, 0.2f, 0.3f); 48 | ASSERT(outOpFMul == vec3(0.1f, -0.4f, 0.3f)); 49 | 50 | // OpUDiv 51 | uvec3 a8 = uvec3(10, 20, 3); 52 | uvec3 outOpUDiv = a8 / uvec3(4, 5, 6); 53 | ASSERT(outOpUDiv == uvec3(2u, 4u, 0u)); 54 | 55 | // OpSDiv 56 | ivec3 a9 = ivec3(10, -20, 30); 57 | ivec3 outOpSDiv = a9 / ivec3(4, 5, 6); 58 | ASSERT(outOpSDiv == ivec3(2, -4, 5)); 59 | 60 | // OpFDiv 61 | vec3 a10 = vec3(10.f, -20.f, 30.f); 62 | vec3 outOpFDiv = a10 / vec3(4.f, 5.f, 6.f); 63 | ASSERT(outOpFDiv == vec3(2.5f, -4.f, 5.f)); 64 | 65 | // OpUMod 66 | uvec3 a11 = uvec3(10, 21, 31); 67 | uvec3 outOpUMod = a11 % uvec3(4, 5, 6); 68 | ASSERT(outOpUMod == uvec3(2u, 1u, 1u)); 69 | 70 | // OpSMod 71 | ivec3 a12 = ivec3(10, -21, 31); 72 | ivec3 outOpSMod = a12 % ivec3(4, 5, 6); 73 | ASSERT(outOpSMod == ivec3(2, 4, 1)); 74 | 75 | // OpSRem TODO 76 | 77 | // OpFMod 78 | vec3 a13 = vec3(10.1f, -21.4f, 31.3f); 79 | vec3 outOpFMod = mod(a13, vec3(4.f, 5.f, 6.f)); 80 | ASSERT(outOpFMod == vec3(2.1f, 3.6f, 1.3f)); 81 | 82 | // OpFRem TODO 83 | 84 | ASSERT_END 85 | } 86 | -------------------------------------------------------------------------------- /test/assets/arithmetic_0.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/arithmetic_0.frag.spv -------------------------------------------------------------------------------- /test/assets/arithmetic_1.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | void main() 7 | { 8 | ASSERT_BEGIN(32) 9 | 10 | vec4 inVec = vec4(0.2f, 0.3f, 0.4f, 1.f); 11 | 12 | mat4 inMat = mat4(1.f, 1.f, 0.f, 0.f, 13 | 0.f, 1.f, 0.f, 0.f, 14 | 0.f, 0.f, 1.f, 0.f, 15 | 0.f, 0.f, 0.f, 1.f); 16 | 17 | mat4 inMat2 = mat4(1.f, 2.f, 0.f, 0.f, 18 | 0.f, 1.f, 0.f, 0.f, 19 | 0.f, 0.f, 1.f, 0.f, 20 | 0.f, 0.f, 0.f, 1.f); 21 | 22 | // OpVectorTimesScalar 23 | vec4 outOpVectorTimesScalar = inVec * 0.2f; 24 | ASSERT(outOpVectorTimesScalar == vec4(0.04f, 0.06f, 0.08f, 0.2f)); 25 | 26 | // OpMatrixTimesScalar 27 | mat4 outOpMatrixTimesScalar = inMat * 0.2f; 28 | ASSERT(outOpMatrixTimesScalar[0] == vec4(0.2f, 0.2f, 0.f, 0.f)); 29 | ASSERT(outOpMatrixTimesScalar[1] == vec4(0.f, 0.2f, 0.f, 0.f)); 30 | ASSERT(outOpMatrixTimesScalar[2] == vec4(0.f, 0.f, 0.2f, 0.f)); 31 | ASSERT(outOpMatrixTimesScalar[3] == vec4(0.f, 0.f, 0.f, 0.2f)); 32 | 33 | // OpVectorTimesMatrix 34 | vec4 outOpVectorTimesMatrix = inVec * inMat; 35 | ASSERT(outOpVectorTimesMatrix == vec4(0.5f, 0.3f, 0.4f, 1.f)); 36 | 37 | // OpMatrixTimesVector 38 | vec4 outOpMatrixTimesVector = inMat * inVec; 39 | ASSERT(outOpMatrixTimesVector == vec4(0.2f, 0.5f, 0.4f, 1.f)); 40 | 41 | // OpMatrixTimesMatrix 42 | mat4 inMat22 = mat4(inMat2[0], inMat2[1], inMat2[3], inMat2[2]); 43 | mat4 outOpMatrixTimesMatrix = inMat * inMat22; 44 | ASSERT(outOpMatrixTimesMatrix[0] == vec4(1.f, 3.f, 0.f, 0.f)); 45 | ASSERT(outOpMatrixTimesMatrix[1] == vec4(0.f, 1.f, 0.f, 0.f)); 46 | ASSERT(outOpMatrixTimesMatrix[2] == vec4(0.f, 0.f, 0.f, 1.f)); 47 | ASSERT(outOpMatrixTimesMatrix[3] == vec4(0.f, 0.f, 1.f, 0.f)); 48 | 49 | vec4 v1 = vec4(1.f, 2.f, 3.f, 4.f); 50 | vec3 v2 = vec3(1.f, 2.f, 1.f); 51 | 52 | // SpvOpOuterProduct 53 | mat3x4 outerProduct = outerProduct(v1, v2); 54 | ASSERT(outerProduct[0] == vec4(1.f, 2.f, 3.f, 4.f)); 55 | ASSERT(outerProduct[1] == vec4(2.f, 4.f, 6.f, 8.f)); 56 | ASSERT(outerProduct[2] == vec4(1.f, 2.f, 3.f, 4.f)); 57 | 58 | // SpvOpOuterProduct 59 | vec3 outCross = cross(v1.xyz, v2); 60 | ASSERT(outCross == vec3(-4.f, 2.f, 0.f)); 61 | 62 | // SpvOpDot 63 | float outOpDot = dot(v1.xyz, v2); 64 | ASSERT(outOpDot == 8.f); 65 | 66 | // SpvOpIAddCarry 67 | uint uInteger1 = 0x00000001u; 68 | uint uInteger2 = 0x00000002u; 69 | uint uInteger3 = 0xFFFFFFFEu; 70 | uint carry; 71 | ASSERT(uaddCarry(uInteger1, uInteger3, carry) == 0xFFFFFFFFu); 72 | ASSERT(carry == 0u); 73 | ASSERT(uaddCarry(uInteger2, uInteger3, carry) == 0x00000001u); 74 | ASSERT(carry == 1u); 75 | 76 | // SpvOpISubBorrow 77 | uvec2 borrow; 78 | ASSERT(usubBorrow(uvec2(uInteger1, uInteger2), uvec2(uInteger2, uInteger1), borrow) 79 | == uvec2(0xFFFFFFFEu, 0x00000001u)); 80 | ASSERT(borrow == uvec2(1u, 0u)); 81 | 82 | // SpvOpUMulExtended 83 | uint ux = 0xffffffff; 84 | uint uy = 0x3; 85 | uint umsb = 0; 86 | uint ulsb = 0; 87 | umulExtended(ux, uy, umsb, ulsb); 88 | ASSERT(uvec2(umsb, ulsb) == uvec2(0x2, 0xfffffffd)); 89 | 90 | uvec2 uvx = uvec2(0xffffffff, 0x1); 91 | uvec2 uvy = uvec2(0x3); 92 | uvec2 uvmsb = uvec2(0); 93 | uvec2 uvlsb = uvec2(0); 94 | umulExtended(uvx, uvy, uvmsb, uvlsb); 95 | ASSERT(uvmsb == uvec2(0x2, 0x0)); 96 | ASSERT(uvlsb == uvec2(0xfffffffd, 0x3)); 97 | 98 | // SpvOpSMulExtended 99 | int ix = 0xfffffff; 100 | int iy = 0xfff; 101 | int imsb = 0; 102 | int ilsb = 0; 103 | imulExtended(ix, iy, imsb, ilsb); 104 | ASSERT(ivec2(imsb, ilsb) == ivec2(0xff, 0xeffff001)); 105 | 106 | ivec2 ivx = ivec2(0xfffffff, 0x1); 107 | ivec2 ivy = ivec2(0xfff); 108 | ivec2 ivmsb = ivec2(0); 109 | ivec2 ivlsb = ivec2(0); 110 | imulExtended(ivx, ivy, ivmsb, ivlsb); 111 | ASSERT(ivmsb == ivec2(0xff, 0x0)); 112 | ASSERT(ivlsb == ivec2(0xeffff001, 0xfff)); 113 | 114 | ASSERT_END 115 | } 116 | -------------------------------------------------------------------------------- /test/assets/arithmetic_1.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/arithmetic_1.frag.spv -------------------------------------------------------------------------------- /test/assets/array.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | layout(set = 0, binding = 0) buffer TestBlock 7 | { 8 | vec3 a; 9 | float[] b; 10 | } tb; 11 | 12 | void main() 13 | { 14 | ASSERT_BEGIN(32) 15 | 16 | float arr[5] = float[5](3.4f, 4.2f, 5.0f, 5.2f, 1.1f); 17 | ASSERT(arr[0] == 3.4f); 18 | ASSERT(arr[1] == 4.2f); 19 | ASSERT(arr[2] == 5.0f); 20 | ASSERT(arr[3] == 5.2f); 21 | ASSERT(arr[4] == 1.1f); 22 | ASSERT(arr.length() == 5); 23 | 24 | ASSERT(tb.a == vec3(4.2f, 5.0f, 5.2f)); 25 | 26 | int n = tb.b.length(); 27 | ASSERT(n == 6); 28 | 29 | ASSERT(tb.b[0] == 1.2f); 30 | ASSERT(tb.b[1] == 1.0f); 31 | ASSERT(tb.b[2] == 1.3f); 32 | ASSERT(tb.b[3] == 0.2f); 33 | ASSERT(tb.b[4] == 0.3f); 34 | ASSERT(tb.b[5] == 0.5f); 35 | 36 | ASSERT_END 37 | } 38 | -------------------------------------------------------------------------------- /test/assets/array.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/array.frag.spv -------------------------------------------------------------------------------- /test/assets/assert.glsl: -------------------------------------------------------------------------------- 1 | layout (location = 0) out int outAssert; 2 | 3 | #define ASSERT_BEGIN(n) \ 4 | int assertIdx = 0; \ 5 | bool asserts[n]; 6 | 7 | #define ASSERT(condition) \ 8 | asserts[assertIdx] = (condition); \ 9 | assertIdx++; 10 | 11 | #define ASSERT_END \ 12 | outAssert = -1; \ 13 | for (int i = 0; i < assertIdx; i++) { \ 14 | if (!asserts[i]) { \ 15 | outAssert = i; \ 16 | break; \ 17 | } \ 18 | } 19 | -------------------------------------------------------------------------------- /test/assets/bit.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | void main() 7 | { 8 | ASSERT_BEGIN(64) 9 | 10 | // SpvOpShiftRightLogical 11 | uint ua = 0xfffffffe; 12 | ASSERT((ua >> 1) == 0x7fffffff) 13 | ASSERT((uvec2(ua) >> 1) == uvec2(0x7fffffff)) 14 | 15 | // SpvOpShiftRightArithmetic 16 | int ia1 = -2; // 0xfffffffe 17 | ASSERT((ia1 >> 1) == 0xffffffff) 18 | ASSERT((ivec2(ia1) >> 1) == ivec2(0xffffffff)) 19 | 20 | int ia2 = 0xff; 21 | ASSERT((ia2 >> 1) == 0x7f) 22 | ASSERT((ivec2(ia2) >> 1) == ivec2(0x7f)) 23 | 24 | // SpvOpShiftLeftLogical 25 | ASSERT((ua << 1) == 0xfffffffc) 26 | ASSERT((uvec2(ua) << 1) == uvec2(0xfffffffc)) 27 | 28 | ASSERT((ia1 << 1) == -4) // 0xfffffffc 29 | ASSERT((ivec2(ia1) << 1) == ivec2(-4)) 30 | 31 | // SpvOpBitwiseOr 32 | ASSERT((ua | 0x1) == 0xffffffff) 33 | ASSERT((ua | 0) == 0xfffffffe) 34 | 35 | // SpvOpBitwiseXor 36 | ASSERT((ua ^ 0x1) == 0xffffffff) 37 | ASSERT((ua ^ 0xff) == 0xffffff01) 38 | 39 | // SpvOpBitwiseAnd 40 | ASSERT((ua & 0x1) == 0) 41 | ASSERT((ua & 0xff) == 0xfe) 42 | 43 | // SpvOpNot 44 | ASSERT((~ua) == 0x1) 45 | 46 | // SpvOpBitFieldInsert 47 | ASSERT(bitfieldInsert(0x00000000, 0xffffffff, 0, 32) == 0xffffffff) 48 | ASSERT(bitfieldInsert(0x00000000, 0xffffffff, 0, 31) == 0x7fffffff) 49 | ASSERT(bitfieldInsert(0x00000000, 0xffffffff, 0, 0) == 0x00000000) 50 | ASSERT(bitfieldInsert(0xff000000, 0x000000ff, 8, 8) == 0xff00ff00) 51 | ASSERT(bitfieldInsert(0xffff0000, 0xffff0000, 16, 16) == 0x00000000) 52 | ASSERT(bitfieldInsert(0x0000ffff, 0x0000ffff, 16, 16) == 0xffffffff) 53 | 54 | // SpvOpBitFieldSExtract 55 | ASSERT(bitfieldExtract(0x0f0f0f0f, 0, 32) == 0x0f0f0f0f) 56 | ASSERT(bitfieldExtract(0x000000ff, 1, 3) == 0x00000007) 57 | ASSERT(bitfieldExtract(0x0000ff00, 8, 8) == 0x000000ff) 58 | 59 | // SpvOpBitFieldUExtract 60 | ASSERT(bitfieldExtract(0xffffffffu, 0, 32) == 0xffffffff) 61 | ASSERT(bitfieldExtract(0xfffffff0u, 0, 5) == 0x00000010) 62 | 63 | // SpvOpBitReverse 64 | ASSERT(bitfieldReverse(0x55555555) == 0xAAAAAAAA) 65 | ASSERT(bitfieldReverse(0x0F0F0F0F) == 0xF0F0F0F0) 66 | 67 | // SpvOpBitCount 68 | ASSERT(bitCount(0x00000001) == 1) 69 | ASSERT(bitCount(0x00000003) == 2) 70 | ASSERT(bitCount(0x7fffffff) == 31) 71 | ASSERT(bitCount(0x00000000) == 0) 72 | 73 | ASSERT_END 74 | } 75 | -------------------------------------------------------------------------------- /test/assets/bit.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/bit.frag.spv -------------------------------------------------------------------------------- /test/assets/built_in.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout (location = 0) out vec3 outColor; 4 | 5 | void main() 6 | { 7 | const vec3 positions[3] = vec3[3]( 8 | vec3(1.f, 1.f, 0.0f), 9 | vec3(-1.f, 1.f, 0.0f), 10 | vec3(0.f, -1.f, 0.0f) 11 | ); 12 | 13 | const vec3 colors[3] = vec3[3]( 14 | vec3(1.0f, 0.0f, 0.0f), 15 | vec3(0.0f, 1.0f, 0.0f), 16 | vec3(0.f, 0.0f, 1.0f) 17 | ); 18 | 19 | gl_Position = vec4(positions[gl_VertexIndex], 1.0f); 20 | outColor = colors[gl_VertexIndex]; 21 | } -------------------------------------------------------------------------------- /test/assets/built_in.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/built_in.vert.spv -------------------------------------------------------------------------------- /test/assets/built_in.vert.spv.txt: -------------------------------------------------------------------------------- 1 | built_in.vert 2 | // Module Version 10000 3 | // Generated by (magic number): 8000a 4 | // Id's are bound by 50 5 | 6 | Capability Shader 7 | 1: ExtInstImport "GLSL.std.450" 8 | MemoryModel Logical GLSL450 9 | EntryPoint Vertex 4 "main" 13 27 41 10 | Source GLSL 450 11 | Name 4 "main" 12 | Name 11 "gl_PerVertex" 13 | MemberName 11(gl_PerVertex) 0 "gl_Position" 14 | MemberName 11(gl_PerVertex) 1 "gl_PointSize" 15 | MemberName 11(gl_PerVertex) 2 "gl_ClipDistance" 16 | MemberName 11(gl_PerVertex) 3 "gl_CullDistance" 17 | Name 13 "" 18 | Name 27 "gl_VertexIndex" 19 | Name 30 "indexable" 20 | Name 41 "outColor" 21 | Name 47 "indexable" 22 | MemberDecorate 11(gl_PerVertex) 0 BuiltIn Position 23 | MemberDecorate 11(gl_PerVertex) 1 BuiltIn PointSize 24 | MemberDecorate 11(gl_PerVertex) 2 BuiltIn ClipDistance 25 | MemberDecorate 11(gl_PerVertex) 3 BuiltIn CullDistance 26 | Decorate 11(gl_PerVertex) Block 27 | Decorate 27(gl_VertexIndex) BuiltIn VertexIndex 28 | Decorate 41(outColor) Location 0 29 | 2: TypeVoid 30 | 3: TypeFunction 2 31 | 6: TypeFloat 32 32 | 7: TypeVector 6(float) 4 33 | 8: TypeInt 32 0 34 | 9: 8(int) Constant 1 35 | 10: TypeArray 6(float) 9 36 | 11(gl_PerVertex): TypeStruct 7(fvec4) 6(float) 10 10 37 | 12: TypePointer Output 11(gl_PerVertex) 38 | 13: 12(ptr) Variable Output 39 | 14: TypeInt 32 1 40 | 15: 14(int) Constant 0 41 | 16: TypeVector 6(float) 3 42 | 17: 8(int) Constant 3 43 | 18: TypeArray 16(fvec3) 17 44 | 19: 6(float) Constant 1065353216 45 | 20: 6(float) Constant 0 46 | 21: 16(fvec3) ConstantComposite 19 19 20 47 | 22: 6(float) Constant 3212836864 48 | 23: 16(fvec3) ConstantComposite 22 19 20 49 | 24: 16(fvec3) ConstantComposite 20 22 20 50 | 25: 18 ConstantComposite 21 23 24 51 | 26: TypePointer Input 14(int) 52 | 27(gl_VertexIndex): 26(ptr) Variable Input 53 | 29: TypePointer Function 18 54 | 31: TypePointer Function 16(fvec3) 55 | 38: TypePointer Output 7(fvec4) 56 | 40: TypePointer Output 16(fvec3) 57 | 41(outColor): 40(ptr) Variable Output 58 | 42: 16(fvec3) ConstantComposite 19 20 20 59 | 43: 16(fvec3) ConstantComposite 20 19 20 60 | 44: 16(fvec3) ConstantComposite 20 20 19 61 | 45: 18 ConstantComposite 42 43 44 62 | 4(main): 2 Function None 3 63 | 5: Label 64 | 30(indexable): 29(ptr) Variable Function 65 | 47(indexable): 29(ptr) Variable Function 66 | 28: 14(int) Load 27(gl_VertexIndex) 67 | Store 30(indexable) 25 68 | 32: 31(ptr) AccessChain 30(indexable) 28 69 | 33: 16(fvec3) Load 32 70 | 34: 6(float) CompositeExtract 33 0 71 | 35: 6(float) CompositeExtract 33 1 72 | 36: 6(float) CompositeExtract 33 2 73 | 37: 7(fvec4) CompositeConstruct 34 35 36 19 74 | 39: 38(ptr) AccessChain 13 15 75 | Store 39 37 76 | 46: 14(int) Load 27(gl_VertexIndex) 77 | Store 47(indexable) 45 78 | 48: 31(ptr) AccessChain 47(indexable) 46 79 | 49: 16(fvec3) Load 48 80 | Store 41(outColor) 49 81 | Return 82 | FunctionEnd 83 | -------------------------------------------------------------------------------- /test/assets/composite.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | void main() 7 | { 8 | ASSERT_BEGIN(32) 9 | 10 | mat4 m1 = mat4(1.f, 2.f, 0.f, 0.f, 11 | 0.f, 1.f, 0.f, 0.f, 12 | 0.f, 0.f, 1.f, 0.f, 13 | 0.f, 0.f, 0.f, 1.f); 14 | 15 | mat4 m2 = transpose(m1); 16 | ASSERT(m2[0] == vec4(1.f, 0.f, 0.f, 0.f)); 17 | ASSERT(m2[1] == vec4(2.f, 1.f, 0.f, 0.f)); 18 | ASSERT(m2[2] == vec4(0.f, 0.f, 1.f, 0.f)); 19 | ASSERT(m2[3] == vec4(0.f, 0.f, 0.f, 1.f)); 20 | 21 | vec4 v1 = vec4(m1[0].yxz, 1.f); 22 | ASSERT(v1 == vec4(2.f, 1.f, 0.f, 1.f)); 23 | 24 | ASSERT_END 25 | } 26 | -------------------------------------------------------------------------------- /test/assets/composite.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/composite.frag.spv -------------------------------------------------------------------------------- /test/assets/composite.frag.spv.txt: -------------------------------------------------------------------------------- 1 | composite.frag 2 | // Module Version 10000 3 | // Generated by (magic number): 8000a 4 | // Id's are bound by 111 5 | 6 | Capability Shader 7 | 1: ExtInstImport "GLSL.std.450" 8 | MemoryModel Logical GLSL450 9 | EntryPoint Fragment 4 "main" 90 10 | ExecutionMode 4 OriginUpperLeft 11 | Source GLSL 450 12 | SourceExtension "GL_GOOGLE_cpp_style_line_directive" 13 | SourceExtension "GL_GOOGLE_include_directive" 14 | Name 4 "main" 15 | Name 8 "assertIdx" 16 | Name 14 "m1" 17 | Name 23 "m2" 18 | Name 31 "asserts" 19 | Name 72 "v1" 20 | Name 90 "outAssert" 21 | Name 92 "i" 22 | Decorate 90(outAssert) Location 0 23 | 2: TypeVoid 24 | 3: TypeFunction 2 25 | 6: TypeInt 32 1 26 | 7: TypePointer Function 6(int) 27 | 9: 6(int) Constant 0 28 | 10: TypeFloat 32 29 | 11: TypeVector 10(float) 4 30 | 12: TypeMatrix 11(fvec4) 4 31 | 13: TypePointer Function 12 32 | 15: 10(float) Constant 1065353216 33 | 16: 10(float) Constant 1073741824 34 | 17: 10(float) Constant 0 35 | 18: 11(fvec4) ConstantComposite 15 16 17 17 36 | 19: 11(fvec4) ConstantComposite 17 15 17 17 37 | 20: 11(fvec4) ConstantComposite 17 17 15 17 38 | 21: 11(fvec4) ConstantComposite 17 17 17 15 39 | 22: 12 ConstantComposite 18 19 20 21 40 | 26: TypeBool 41 | 27: TypeInt 32 0 42 | 28: 27(int) Constant 32 43 | 29: TypeArray 26(bool) 28 44 | 30: TypePointer Function 29 45 | 33: TypePointer Function 11(fvec4) 46 | 36: 11(fvec4) ConstantComposite 15 17 17 17 47 | 37: TypeVector 26(bool) 4 48 | 40: TypePointer Function 26(bool) 49 | 43: 6(int) Constant 1 50 | 48: 11(fvec4) ConstantComposite 16 15 17 17 51 | 55: 6(int) Constant 2 52 | 64: 6(int) Constant 3 53 | 73: TypeVector 10(float) 3 54 | 83: 11(fvec4) ConstantComposite 16 15 17 15 55 | 89: TypePointer Output 6(int) 56 | 90(outAssert): 89(ptr) Variable Output 57 | 91: 6(int) Constant 4294967295 58 | 4(main): 2 Function None 3 59 | 5: Label 60 | 8(assertIdx): 7(ptr) Variable Function 61 | 14(m1): 13(ptr) Variable Function 62 | 23(m2): 13(ptr) Variable Function 63 | 31(asserts): 30(ptr) Variable Function 64 | 72(v1): 33(ptr) Variable Function 65 | 92(i): 7(ptr) Variable Function 66 | Store 8(assertIdx) 9 67 | Store 14(m1) 22 68 | 24: 12 Load 14(m1) 69 | 25: 12 Transpose 24 70 | Store 23(m2) 25 71 | 32: 6(int) Load 8(assertIdx) 72 | 34: 33(ptr) AccessChain 23(m2) 9 73 | 35: 11(fvec4) Load 34 74 | 38: 37(bvec4) FOrdEqual 35 36 75 | 39: 26(bool) All 38 76 | 41: 40(ptr) AccessChain 31(asserts) 32 77 | Store 41 39 78 | 42: 6(int) Load 8(assertIdx) 79 | 44: 6(int) IAdd 42 43 80 | Store 8(assertIdx) 44 81 | 45: 6(int) Load 8(assertIdx) 82 | 46: 33(ptr) AccessChain 23(m2) 43 83 | 47: 11(fvec4) Load 46 84 | 49: 37(bvec4) FOrdEqual 47 48 85 | 50: 26(bool) All 49 86 | 51: 40(ptr) AccessChain 31(asserts) 45 87 | Store 51 50 88 | 52: 6(int) Load 8(assertIdx) 89 | 53: 6(int) IAdd 52 43 90 | Store 8(assertIdx) 53 91 | 54: 6(int) Load 8(assertIdx) 92 | 56: 33(ptr) AccessChain 23(m2) 55 93 | 57: 11(fvec4) Load 56 94 | 58: 37(bvec4) FOrdEqual 57 20 95 | 59: 26(bool) All 58 96 | 60: 40(ptr) AccessChain 31(asserts) 54 97 | Store 60 59 98 | 61: 6(int) Load 8(assertIdx) 99 | 62: 6(int) IAdd 61 43 100 | Store 8(assertIdx) 62 101 | 63: 6(int) Load 8(assertIdx) 102 | 65: 33(ptr) AccessChain 23(m2) 64 103 | 66: 11(fvec4) Load 65 104 | 67: 37(bvec4) FOrdEqual 66 21 105 | 68: 26(bool) All 67 106 | 69: 40(ptr) AccessChain 31(asserts) 63 107 | Store 69 68 108 | 70: 6(int) Load 8(assertIdx) 109 | 71: 6(int) IAdd 70 43 110 | Store 8(assertIdx) 71 111 | 74: 33(ptr) AccessChain 14(m1) 9 112 | 75: 11(fvec4) Load 74 113 | 76: 73(fvec3) VectorShuffle 75 75 1 0 2 114 | 77: 10(float) CompositeExtract 76 0 115 | 78: 10(float) CompositeExtract 76 1 116 | 79: 10(float) CompositeExtract 76 2 117 | 80: 11(fvec4) CompositeConstruct 77 78 79 15 118 | Store 72(v1) 80 119 | 81: 6(int) Load 8(assertIdx) 120 | 82: 11(fvec4) Load 72(v1) 121 | 84: 37(bvec4) FOrdEqual 82 83 122 | 85: 26(bool) All 84 123 | 86: 40(ptr) AccessChain 31(asserts) 81 124 | Store 86 85 125 | 87: 6(int) Load 8(assertIdx) 126 | 88: 6(int) IAdd 87 43 127 | Store 8(assertIdx) 88 128 | Store 90(outAssert) 91 129 | Store 92(i) 9 130 | Branch 93 131 | 93: Label 132 | LoopMerge 95 96 None 133 | Branch 97 134 | 97: Label 135 | 98: 6(int) Load 92(i) 136 | 99: 6(int) Load 8(assertIdx) 137 | 100: 26(bool) SLessThan 98 99 138 | BranchConditional 100 94 95 139 | 94: Label 140 | 101: 6(int) Load 92(i) 141 | 102: 40(ptr) AccessChain 31(asserts) 101 142 | 103: 26(bool) Load 102 143 | 104: 26(bool) LogicalNot 103 144 | SelectionMerge 106 None 145 | BranchConditional 104 105 106 146 | 105: Label 147 | 107: 6(int) Load 92(i) 148 | Store 90(outAssert) 107 149 | Branch 95 150 | 106: Label 151 | Branch 96 152 | 96: Label 153 | 109: 6(int) Load 92(i) 154 | 110: 6(int) IAdd 109 43 155 | Store 92(i) 110 156 | Branch 93 157 | 95: Label 158 | Return 159 | FunctionEnd 160 | -------------------------------------------------------------------------------- /test/assets/control_flow.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | vec4 func_if(vec3 inColor) { 7 | vec4 outColor; 8 | if (inColor.r > 0.5f) { 9 | outColor = vec4(inColor, 1.f); 10 | } else { 11 | outColor = vec4(inColor, 0.f); 12 | } 13 | return outColor; 14 | } 15 | 16 | float func_switch(vec3 inColor) { 17 | float outColor = 0.f; 18 | switch (int(inColor.g)) { 19 | case 0: outColor = 0.1f; break; 20 | case 1: outColor = 0.2f; break; 21 | case 2: outColor = 0.3f; break; 22 | default :outColor = 0.4f; break; 23 | } 24 | return outColor; 25 | } 26 | 27 | void main() 28 | { 29 | ASSERT_BEGIN(32) 30 | 31 | // if switch 32 | vec3 inColor = vec3(0.2f, 0.3f, 0.4f); 33 | vec4 outColor0 = func_if(inColor); 34 | float outColor1 = func_switch(inColor); 35 | ASSERT(outColor0 == vec4(0.2f, 0.3f, 0.4f, 0.f)); 36 | ASSERT(outColor1 == 0.1f); 37 | 38 | inColor[0] = 0.6f; 39 | inColor[1] = 2.0f; 40 | outColor0 = func_if(inColor); 41 | outColor1 = func_switch(inColor); 42 | ASSERT(outColor0 == vec4(0.6f, 2.0f, 0.4f, 1.f)); 43 | ASSERT(outColor1 == 0.3f); 44 | 45 | inColor[1] = 4.0f; 46 | outColor0 = func_if(inColor); 47 | outColor1 = func_switch(inColor); 48 | ASSERT(outColor0 == vec4(0.6f, 4.0f, 0.4f, 1.f)); 49 | ASSERT(outColor1 == 0.4f); 50 | 51 | // phi 52 | vec3 rgb = vec3(0); 53 | if (inColor[0] >= 0.9f || inColor[1] >= 0.9f) { 54 | rgb = outColor0.yxz; 55 | } else { 56 | rgb = inColor; 57 | } 58 | ASSERT(rgb == vec3(4.0f, 0.6f, 0.4f)) 59 | 60 | ASSERT_END 61 | } 62 | -------------------------------------------------------------------------------- /test/assets/control_flow.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/control_flow.frag.spv -------------------------------------------------------------------------------- /test/assets/conversion.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | void main() 7 | { 8 | ASSERT_BEGIN(32) 9 | 10 | // OpConvertFToU 11 | float f0 = 12.5f; 12 | uint outUint0 = uint(f0); 13 | ASSERT(outUint0 == 12u); 14 | 15 | // OpConvertFToS 16 | float f1 = -11.3f; 17 | int outInt0 = int(f1); 18 | ASSERT(outInt0 == -11); 19 | 20 | // OpConvertUToF 21 | float outFloat0 = float(outUint0); 22 | ASSERT(outFloat0 == 12.f); 23 | 24 | // OpConvertSToF 25 | float outFloat1 = float(outInt0); 26 | ASSERT(outFloat1 == -11.f); 27 | 28 | // OpBitcast 29 | uint u32 = 10; 30 | int outInt2 = int(u32); 31 | ASSERT(outInt2 == 10); 32 | 33 | // OpFConvert TODO 34 | 35 | ASSERT_END 36 | } 37 | -------------------------------------------------------------------------------- /test/assets/conversion.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/conversion.frag.spv -------------------------------------------------------------------------------- /test/assets/conversion.frag.spv.txt: -------------------------------------------------------------------------------- 1 | .\conversion.frag 2 | // Module Version 10000 3 | // Generated by (magic number): 8000a 4 | // Id's are bound by 99 5 | 6 | Capability Shader 7 | 1: ExtInstImport "GLSL.std.450" 8 | MemoryModel Logical GLSL450 9 | EntryPoint Fragment 4 "main" 78 10 | ExecutionMode 4 OriginUpperLeft 11 | Source GLSL 450 12 | SourceExtension "GL_GOOGLE_cpp_style_line_directive" 13 | SourceExtension "GL_GOOGLE_include_directive" 14 | Name 4 "main" 15 | Name 8 "assertIdx" 16 | Name 12 "f0" 17 | Name 16 "outUint0" 18 | Name 23 "asserts" 19 | Name 33 "f1" 20 | Name 35 "outInt0" 21 | Name 45 "outFloat0" 22 | Name 55 "outFloat1" 23 | Name 65 "u32" 24 | Name 67 "outInt2" 25 | Name 78 "outAssert" 26 | Name 80 "i" 27 | Decorate 78(outAssert) Location 0 28 | 2: TypeVoid 29 | 3: TypeFunction 2 30 | 6: TypeInt 32 1 31 | 7: TypePointer Function 6(int) 32 | 9: 6(int) Constant 0 33 | 10: TypeFloat 32 34 | 11: TypePointer Function 10(float) 35 | 13: 10(float) Constant 1095237632 36 | 14: TypeInt 32 0 37 | 15: TypePointer Function 14(int) 38 | 19: TypeBool 39 | 20: 14(int) Constant 32 40 | 21: TypeArray 19(bool) 20 41 | 22: TypePointer Function 21 42 | 26: 14(int) Constant 12 43 | 28: TypePointer Function 19(bool) 44 | 31: 6(int) Constant 1 45 | 34: 10(float) Constant 3241462989 46 | 40: 6(int) Constant 4294967285 47 | 50: 10(float) Constant 1094713344 48 | 60: 10(float) Constant 3241148416 49 | 66: 14(int) Constant 10 50 | 72: 6(int) Constant 10 51 | 77: TypePointer Output 6(int) 52 | 78(outAssert): 77(ptr) Variable Output 53 | 79: 6(int) Constant 4294967295 54 | 4(main): 2 Function None 3 55 | 5: Label 56 | 8(assertIdx): 7(ptr) Variable Function 57 | 12(f0): 11(ptr) Variable Function 58 | 16(outUint0): 15(ptr) Variable Function 59 | 23(asserts): 22(ptr) Variable Function 60 | 33(f1): 11(ptr) Variable Function 61 | 35(outInt0): 7(ptr) Variable Function 62 | 45(outFloat0): 11(ptr) Variable Function 63 | 55(outFloat1): 11(ptr) Variable Function 64 | 65(u32): 15(ptr) Variable Function 65 | 67(outInt2): 7(ptr) Variable Function 66 | 80(i): 7(ptr) Variable Function 67 | Store 8(assertIdx) 9 68 | Store 12(f0) 13 69 | 17: 10(float) Load 12(f0) 70 | 18: 14(int) ConvertFToU 17 71 | Store 16(outUint0) 18 72 | 24: 6(int) Load 8(assertIdx) 73 | 25: 14(int) Load 16(outUint0) 74 | 27: 19(bool) IEqual 25 26 75 | 29: 28(ptr) AccessChain 23(asserts) 24 76 | Store 29 27 77 | 30: 6(int) Load 8(assertIdx) 78 | 32: 6(int) IAdd 30 31 79 | Store 8(assertIdx) 32 80 | Store 33(f1) 34 81 | 36: 10(float) Load 33(f1) 82 | 37: 6(int) ConvertFToS 36 83 | Store 35(outInt0) 37 84 | 38: 6(int) Load 8(assertIdx) 85 | 39: 6(int) Load 35(outInt0) 86 | 41: 19(bool) IEqual 39 40 87 | 42: 28(ptr) AccessChain 23(asserts) 38 88 | Store 42 41 89 | 43: 6(int) Load 8(assertIdx) 90 | 44: 6(int) IAdd 43 31 91 | Store 8(assertIdx) 44 92 | 46: 14(int) Load 16(outUint0) 93 | 47: 10(float) ConvertUToF 46 94 | Store 45(outFloat0) 47 95 | 48: 6(int) Load 8(assertIdx) 96 | 49: 10(float) Load 45(outFloat0) 97 | 51: 19(bool) FOrdEqual 49 50 98 | 52: 28(ptr) AccessChain 23(asserts) 48 99 | Store 52 51 100 | 53: 6(int) Load 8(assertIdx) 101 | 54: 6(int) IAdd 53 31 102 | Store 8(assertIdx) 54 103 | 56: 6(int) Load 35(outInt0) 104 | 57: 10(float) ConvertSToF 56 105 | Store 55(outFloat1) 57 106 | 58: 6(int) Load 8(assertIdx) 107 | 59: 10(float) Load 55(outFloat1) 108 | 61: 19(bool) FOrdEqual 59 60 109 | 62: 28(ptr) AccessChain 23(asserts) 58 110 | Store 62 61 111 | 63: 6(int) Load 8(assertIdx) 112 | 64: 6(int) IAdd 63 31 113 | Store 8(assertIdx) 64 114 | Store 65(u32) 66 115 | 68: 14(int) Load 65(u32) 116 | 69: 6(int) Bitcast 68 117 | Store 67(outInt2) 69 118 | 70: 6(int) Load 8(assertIdx) 119 | 71: 6(int) Load 67(outInt2) 120 | 73: 19(bool) IEqual 71 72 121 | 74: 28(ptr) AccessChain 23(asserts) 70 122 | Store 74 73 123 | 75: 6(int) Load 8(assertIdx) 124 | 76: 6(int) IAdd 75 31 125 | Store 8(assertIdx) 76 126 | Store 78(outAssert) 79 127 | Store 80(i) 9 128 | Branch 81 129 | 81: Label 130 | LoopMerge 83 84 None 131 | Branch 85 132 | 85: Label 133 | 86: 6(int) Load 80(i) 134 | 87: 6(int) Load 8(assertIdx) 135 | 88: 19(bool) SLessThan 86 87 136 | BranchConditional 88 82 83 137 | 82: Label 138 | 89: 6(int) Load 80(i) 139 | 90: 28(ptr) AccessChain 23(asserts) 89 140 | 91: 19(bool) Load 90 141 | 92: 19(bool) LogicalNot 91 142 | SelectionMerge 94 None 143 | BranchConditional 92 93 94 144 | 93: Label 145 | 95: 6(int) Load 80(i) 146 | Store 78(outAssert) 95 147 | Branch 83 148 | 94: Label 149 | Branch 84 150 | 84: Label 151 | 97: 6(int) Load 80(i) 152 | 98: 6(int) IAdd 97 31 153 | Store 80(i) 98 154 | Branch 81 155 | 83: Label 156 | Return 157 | FunctionEnd 158 | -------------------------------------------------------------------------------- /test/assets/derivative.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout (location = 0) in vec3 inColor; 4 | layout (location = 0) out vec4 outDx; 5 | layout (location = 1) out vec4 outDy; 6 | 7 | void main() 8 | { 9 | vec3 m = inColor.xzy; 10 | vec3 dx = dFdx(m); 11 | vec3 dy = dFdy(m); 12 | outDx = vec4(dx, 1.0f); 13 | outDy = vec4(dy, 1.0f); 14 | } 15 | -------------------------------------------------------------------------------- /test/assets/derivative.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/derivative.frag.spv -------------------------------------------------------------------------------- /test/assets/derivative.frag.spv.txt: -------------------------------------------------------------------------------- 1 | .\derivative.frag 2 | // Module Version 10000 3 | // Generated by (magic number): 8000a 4 | // Id's are bound by 35 5 | 6 | Capability Shader 7 | 1: ExtInstImport "GLSL.std.450" 8 | MemoryModel Logical GLSL450 9 | EntryPoint Fragment 4 "main" 11 22 29 10 | ExecutionMode 4 OriginUpperLeft 11 | Source GLSL 450 12 | Name 4 "main" 13 | Name 9 "m" 14 | Name 11 "inColor" 15 | Name 14 "dx" 16 | Name 17 "dy" 17 | Name 22 "outDx" 18 | Name 29 "outDy" 19 | Decorate 11(inColor) Location 0 20 | Decorate 22(outDx) Location 0 21 | Decorate 29(outDy) Location 1 22 | 2: TypeVoid 23 | 3: TypeFunction 2 24 | 6: TypeFloat 32 25 | 7: TypeVector 6(float) 3 26 | 8: TypePointer Function 7(fvec3) 27 | 10: TypePointer Input 7(fvec3) 28 | 11(inColor): 10(ptr) Variable Input 29 | 20: TypeVector 6(float) 4 30 | 21: TypePointer Output 20(fvec4) 31 | 22(outDx): 21(ptr) Variable Output 32 | 24: 6(float) Constant 1065353216 33 | 29(outDy): 21(ptr) Variable Output 34 | 4(main): 2 Function None 3 35 | 5: Label 36 | 9(m): 8(ptr) Variable Function 37 | 14(dx): 8(ptr) Variable Function 38 | 17(dy): 8(ptr) Variable Function 39 | 12: 7(fvec3) Load 11(inColor) 40 | 13: 7(fvec3) VectorShuffle 12 12 0 2 1 41 | Store 9(m) 13 42 | 15: 7(fvec3) Load 9(m) 43 | 16: 7(fvec3) DPdx 15 44 | Store 14(dx) 16 45 | 18: 7(fvec3) Load 9(m) 46 | 19: 7(fvec3) DPdy 18 47 | Store 17(dy) 19 48 | 23: 7(fvec3) Load 14(dx) 49 | 25: 6(float) CompositeExtract 23 0 50 | 26: 6(float) CompositeExtract 23 1 51 | 27: 6(float) CompositeExtract 23 2 52 | 28: 20(fvec4) CompositeConstruct 25 26 27 24 53 | Store 22(outDx) 28 54 | 30: 7(fvec3) Load 17(dy) 55 | 31: 6(float) CompositeExtract 30 0 56 | 32: 6(float) CompositeExtract 30 1 57 | 33: 6(float) CompositeExtract 30 2 58 | 34: 20(fvec4) CompositeConstruct 31 32 33 24 59 | Store 29(outDy) 34 60 | Return 61 | FunctionEnd 62 | -------------------------------------------------------------------------------- /test/assets/function.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | float func1(float a, float b) { 7 | return a - b; 8 | } 9 | 10 | vec3 func2(vec3 a) { 11 | return a * 0.2f; 12 | } 13 | 14 | void main() 15 | { 16 | ASSERT_BEGIN(32) 17 | 18 | vec3 inColor = vec3(0.2f, 0.3f, 0.4f); 19 | float outColor0 = func1(1.f - inColor.r, func2(inColor).g); 20 | ASSERT(outColor0 == 0.74f) 21 | 22 | vec4 outColor1 = vec4(func2(inColor), func1(1.0f, inColor.r)); 23 | ASSERT(outColor1 == vec4(0.04f, 0.06f, 0.08f, 0.8f)) 24 | 25 | ASSERT_END 26 | } 27 | -------------------------------------------------------------------------------- /test/assets/function.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/function.frag.spv -------------------------------------------------------------------------------- /test/assets/function.frag.spv.txt: -------------------------------------------------------------------------------- 1 | .\function.frag 2 | // Module Version 10000 3 | // Generated by (magic number): 8000a 4 | // Id's are bound by 116 5 | 6 | Capability Shader 7 | 1: ExtInstImport "GLSL.std.450" 8 | MemoryModel Logical GLSL450 9 | EntryPoint Fragment 4 "main" 95 10 | ExecutionMode 4 OriginUpperLeft 11 | Source GLSL 450 12 | SourceExtension "GL_GOOGLE_cpp_style_line_directive" 13 | SourceExtension "GL_GOOGLE_include_directive" 14 | Name 4 "main" 15 | Name 11 "func1(f1;f1;" 16 | Name 9 "a" 17 | Name 10 "b" 18 | Name 17 "func2(vf3;" 19 | Name 16 "a" 20 | Name 31 "assertIdx" 21 | Name 33 "inColor" 22 | Name 37 "outColor0" 23 | Name 44 "param" 24 | Name 47 "param" 25 | Name 48 "param" 26 | Name 56 "asserts" 27 | Name 68 "outColor1" 28 | Name 69 "param" 29 | Name 72 "param" 30 | Name 73 "param" 31 | Name 95 "outAssert" 32 | Name 97 "i" 33 | Decorate 95(outAssert) Location 0 34 | 2: TypeVoid 35 | 3: TypeFunction 2 36 | 6: TypeFloat 32 37 | 7: TypePointer Function 6(float) 38 | 8: TypeFunction 6(float) 7(ptr) 7(ptr) 39 | 13: TypeVector 6(float) 3 40 | 14: TypePointer Function 13(fvec3) 41 | 15: TypeFunction 13(fvec3) 14(ptr) 42 | 25: 6(float) Constant 1045220557 43 | 29: TypeInt 32 1 44 | 30: TypePointer Function 29(int) 45 | 32: 29(int) Constant 0 46 | 34: 6(float) Constant 1050253722 47 | 35: 6(float) Constant 1053609165 48 | 36: 13(fvec3) ConstantComposite 25 34 35 49 | 38: 6(float) Constant 1065353216 50 | 39: TypeInt 32 0 51 | 40: 39(int) Constant 0 52 | 49: 39(int) Constant 1 53 | 52: TypeBool 54 | 53: 39(int) Constant 32 55 | 54: TypeArray 52(bool) 53 56 | 55: TypePointer Function 54 57 | 59: 6(float) Constant 1060991140 58 | 61: TypePointer Function 52(bool) 59 | 64: 29(int) Constant 1 60 | 66: TypeVector 6(float) 4 61 | 67: TypePointer Function 66(fvec4) 62 | 83: 6(float) Constant 1025758986 63 | 84: 6(float) Constant 1031127695 64 | 85: 6(float) Constant 1034147594 65 | 86: 6(float) Constant 1061997773 66 | 87: 66(fvec4) ConstantComposite 83 84 85 86 67 | 88: TypeVector 52(bool) 4 68 | 94: TypePointer Output 29(int) 69 | 95(outAssert): 94(ptr) Variable Output 70 | 96: 29(int) Constant 4294967295 71 | 4(main): 2 Function None 3 72 | 5: Label 73 | 31(assertIdx): 30(ptr) Variable Function 74 | 33(inColor): 14(ptr) Variable Function 75 | 37(outColor0): 7(ptr) Variable Function 76 | 44(param): 14(ptr) Variable Function 77 | 47(param): 7(ptr) Variable Function 78 | 48(param): 7(ptr) Variable Function 79 | 56(asserts): 55(ptr) Variable Function 80 | 68(outColor1): 67(ptr) Variable Function 81 | 69(param): 14(ptr) Variable Function 82 | 72(param): 7(ptr) Variable Function 83 | 73(param): 7(ptr) Variable Function 84 | 97(i): 30(ptr) Variable Function 85 | Store 31(assertIdx) 32 86 | Store 33(inColor) 36 87 | 41: 7(ptr) AccessChain 33(inColor) 40 88 | 42: 6(float) Load 41 89 | 43: 6(float) FSub 38 42 90 | 45: 13(fvec3) Load 33(inColor) 91 | Store 44(param) 45 92 | 46: 13(fvec3) FunctionCall 17(func2(vf3;) 44(param) 93 | Store 47(param) 43 94 | 50: 6(float) CompositeExtract 46 1 95 | Store 48(param) 50 96 | 51: 6(float) FunctionCall 11(func1(f1;f1;) 47(param) 48(param) 97 | Store 37(outColor0) 51 98 | 57: 29(int) Load 31(assertIdx) 99 | 58: 6(float) Load 37(outColor0) 100 | 60: 52(bool) FOrdEqual 58 59 101 | 62: 61(ptr) AccessChain 56(asserts) 57 102 | Store 62 60 103 | 63: 29(int) Load 31(assertIdx) 104 | 65: 29(int) IAdd 63 64 105 | Store 31(assertIdx) 65 106 | 70: 13(fvec3) Load 33(inColor) 107 | Store 69(param) 70 108 | 71: 13(fvec3) FunctionCall 17(func2(vf3;) 69(param) 109 | Store 72(param) 38 110 | 74: 7(ptr) AccessChain 33(inColor) 40 111 | 75: 6(float) Load 74 112 | Store 73(param) 75 113 | 76: 6(float) FunctionCall 11(func1(f1;f1;) 72(param) 73(param) 114 | 77: 6(float) CompositeExtract 71 0 115 | 78: 6(float) CompositeExtract 71 1 116 | 79: 6(float) CompositeExtract 71 2 117 | 80: 66(fvec4) CompositeConstruct 77 78 79 76 118 | Store 68(outColor1) 80 119 | 81: 29(int) Load 31(assertIdx) 120 | 82: 66(fvec4) Load 68(outColor1) 121 | 89: 88(bvec4) FOrdEqual 82 87 122 | 90: 52(bool) All 89 123 | 91: 61(ptr) AccessChain 56(asserts) 81 124 | Store 91 90 125 | 92: 29(int) Load 31(assertIdx) 126 | 93: 29(int) IAdd 92 64 127 | Store 31(assertIdx) 93 128 | Store 95(outAssert) 96 129 | Store 97(i) 32 130 | Branch 98 131 | 98: Label 132 | LoopMerge 100 101 None 133 | Branch 102 134 | 102: Label 135 | 103: 29(int) Load 97(i) 136 | 104: 29(int) Load 31(assertIdx) 137 | 105: 52(bool) SLessThan 103 104 138 | BranchConditional 105 99 100 139 | 99: Label 140 | 106: 29(int) Load 97(i) 141 | 107: 61(ptr) AccessChain 56(asserts) 106 142 | 108: 52(bool) Load 107 143 | 109: 52(bool) LogicalNot 108 144 | SelectionMerge 111 None 145 | BranchConditional 109 110 111 146 | 110: Label 147 | 112: 29(int) Load 97(i) 148 | Store 95(outAssert) 112 149 | Branch 100 150 | 111: Label 151 | Branch 101 152 | 101: Label 153 | 114: 29(int) Load 97(i) 154 | 115: 29(int) IAdd 114 64 155 | Store 97(i) 115 156 | Branch 98 157 | 100: Label 158 | Return 159 | FunctionEnd 160 | 11(func1(f1;f1;): 6(float) Function None 8 161 | 9(a): 7(ptr) FunctionParameter 162 | 10(b): 7(ptr) FunctionParameter 163 | 12: Label 164 | 19: 6(float) Load 9(a) 165 | 20: 6(float) Load 10(b) 166 | 21: 6(float) FSub 19 20 167 | ReturnValue 21 168 | FunctionEnd 169 | 17(func2(vf3;): 13(fvec3) Function None 15 170 | 16(a): 14(ptr) FunctionParameter 171 | 18: Label 172 | 24: 13(fvec3) Load 16(a) 173 | 26: 13(fvec3) VectorTimesScalar 24 25 174 | ReturnValue 26 175 | FunctionEnd 176 | -------------------------------------------------------------------------------- /test/assets/glsl_std_450_0.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | #define PI 3.1415926535f 7 | 8 | void main() 9 | { 10 | ASSERT_BEGIN(64) 11 | 12 | // Round 13 | float a = 0.f; 14 | ASSERT(round(a) == 0.f) 15 | a = 0.5f; 16 | ASSERT(round(a) == 1.f) 17 | a = 0.9f; 18 | ASSERT(round(a) == 1.f) 19 | a = -0.f; 20 | ASSERT(round(a) == 0.f) 21 | a = -0.5f; 22 | ASSERT(round(a) == -1.f) 23 | a = -0.9f; 24 | ASSERT(round(a) == -1.f) 25 | 26 | // RoundEven 27 | a = -1.5f; 28 | ASSERT(roundEven(a) == -2.f) 29 | a = 1.5f; 30 | ASSERT(roundEven(a) == 2.f) 31 | a = -2.5f; 32 | ASSERT(roundEven(a) == -2.f) 33 | a = 2.5f; 34 | ASSERT(roundEven(a) == 2.f) 35 | a = -2.6f; 36 | ASSERT(roundEven(a) == -3.f) 37 | a = 2.6f; 38 | ASSERT(roundEven(a) == 3.f) 39 | 40 | // Trunc 41 | a = 1.1f; 42 | ASSERT(trunc(a) == 1.f) 43 | a = -1.1f; 44 | ASSERT(trunc(a) == -1.f) 45 | a = 1.9f; 46 | ASSERT(trunc(a) == 1.f) 47 | a = -1.9f; 48 | ASSERT(trunc(a) == -1.f) 49 | 50 | // FAbs 51 | a = -1.f; 52 | ASSERT(abs(a) == 1.f) 53 | 54 | // SAbs 55 | ivec2 b = ivec2(-2); 56 | ASSERT(abs(b) == ivec2(2)) 57 | 58 | // FSign 59 | a = 2.f; 60 | ASSERT(sign(a) == 1.f) 61 | a = 0.f; 62 | ASSERT(sign(a) == 0.f) 63 | a = -2.f; 64 | ASSERT(sign(a) == -1.f) 65 | 66 | // SSign 67 | b = ivec2(-2); 68 | ASSERT(sign(b) == ivec2(-1)) 69 | b = ivec2(0); 70 | ASSERT(sign(b) == ivec2(0)) 71 | b = ivec2(2); 72 | ASSERT(sign(b) == ivec2(1)) 73 | 74 | // Floor 75 | a = 2.8f; 76 | ASSERT(floor(a) == 2.f) 77 | a = -2.8f; 78 | ASSERT(floor(a) == -3.f) 79 | 80 | // Ceil 81 | a = 2.8f; 82 | ASSERT(ceil(a) == 3.f) 83 | a = -2.8f; 84 | ASSERT(ceil(a) == -2.f) 85 | 86 | // Fract 87 | a = 2.8f; 88 | ASSERT(fract(a) == 0.8f) 89 | a = -2.8f; 90 | ASSERT(fract(a) == 0.2f) 91 | 92 | // Radians 93 | a = 180.f; 94 | ASSERT(radians(a) == PI) 95 | 96 | // Degrees 97 | a = PI; 98 | ASSERT(degrees(a) == 180.f) 99 | 100 | // Sin 101 | a = PI; 102 | ASSERT(sin(a) == 0.f) 103 | 104 | // Cos 105 | a = PI; 106 | ASSERT(cos(a) == -1.f) 107 | 108 | // Tan 109 | a = 0.f; 110 | ASSERT(tan(a) == 0.f) 111 | 112 | // Asin 113 | a = 0.f; 114 | ASSERT(asin(a) == 0.f) 115 | 116 | // Acos 117 | a = 1.f; 118 | ASSERT(acos(a) == 0.f) 119 | 120 | // Atan 121 | a = 0.f; 122 | ASSERT(atan(a) == 0.f) 123 | 124 | // Sinh 125 | a = 0.f; 126 | ASSERT(sinh(a) == 0.f) 127 | 128 | // Cosh 129 | a = 0.f; 130 | ASSERT(cosh(a) == 1.f) 131 | 132 | // Tanh 133 | a = 0.f; 134 | ASSERT(tanh(a) == 0.f) 135 | 136 | // Asinh 137 | a = 0.f; 138 | ASSERT(asinh(a) == 0.f) 139 | 140 | // Acosh 141 | a = 1.f; 142 | ASSERT(acosh(a) == 0.f) 143 | 144 | // Atanh 145 | a = 0.f; 146 | ASSERT(atanh(a) == 0.f) 147 | 148 | // Atan2 149 | a = 0.f; 150 | ASSERT(atan(a, 1.f) == 0.f) 151 | 152 | // Pow 153 | a = 2.f; 154 | ASSERT(pow(a, 3) == 8.f) 155 | 156 | // Exp 157 | a = 2.f; 158 | ASSERT(exp(a) == 7.38905609893f) 159 | 160 | // Log 161 | a = 7.38905609893f; 162 | ASSERT(log(a) == 2.f) 163 | 164 | // Exp2 165 | a = 2.f; 166 | ASSERT(exp2(a) == 4.f) 167 | 168 | // Log2 169 | a = 8.f; 170 | ASSERT(log2(a) == 3.f) 171 | 172 | // Sqrt 173 | a = 16.f; 174 | ASSERT(sqrt(a) == 4.f) 175 | 176 | // InverseSqrt 177 | a = 16.f; 178 | ASSERT(inversesqrt(a) == 0.25f) 179 | 180 | ASSERT_END 181 | } 182 | -------------------------------------------------------------------------------- /test/assets/glsl_std_450_0.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/glsl_std_450_0.frag.spv -------------------------------------------------------------------------------- /test/assets/glsl_std_450_1.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | #define PI 3.1415926535f 7 | 8 | void main() 9 | { 10 | ASSERT_BEGIN(64) 11 | 12 | // Determinant 13 | mat2 m1 = mat2(1.f, 0.f, 1.f, 1.f); 14 | float det = determinant(m1); 15 | ASSERT(det == 1.f) 16 | 17 | // MatrixInverse 18 | mat2 m2 = inverse(m1); 19 | ASSERT(m2 == mat2(1.f, 0.f, -1.f, 1.f)); 20 | 21 | float f1 = 1.5f; 22 | float f2 = 0.f; 23 | uint u1 = 1; 24 | uint u2 = 2; 25 | int i1 = -1; 26 | int i2 = -2; 27 | 28 | // Modf 29 | ASSERT(modf(f1, f2) == 0.5f) 30 | ASSERT(f2 == 1.f) 31 | 32 | // ModfStruct TODO 33 | 34 | // FMin 35 | f1 = 1.5f; 36 | f2 = 0.f; 37 | ASSERT(min(f1, f2) == 0.f) 38 | 39 | // UMin 40 | ASSERT(min(u1, u2) == 1) 41 | 42 | // SMin 43 | ASSERT(min(i1, i2) == -2) 44 | 45 | // FMax 46 | ASSERT(max(f1, f2) == 1.5f) 47 | 48 | // UMax 49 | ASSERT(max(u1, u2) == 2) 50 | 51 | // SMax 52 | ASSERT(max(i1, i2) == -1) 53 | 54 | // FClamp 55 | ASSERT(clamp(f1, 0.f, 1.f) == 1.f) 56 | 57 | // UClamp 58 | ASSERT(clamp(u1, 0, 1) == 1) 59 | 60 | // SClamp 61 | ASSERT(clamp(i1, 0, 1) == 0) 62 | 63 | // FMix 64 | ASSERT(mix(f1, f2, 0.f) == f1) 65 | ASSERT(mix(f1, f2, 1.f) == f2) 66 | ASSERT(mix(f1, f2, 0.5f) == 0.75f) 67 | 68 | // IMix skip 69 | 70 | // Step 71 | ASSERT(step(1.4f, f1) == 1.f) 72 | ASSERT(step(1.6f, f1) == 0.f) 73 | 74 | // SmoothStep 75 | ASSERT(smoothstep(0.f, 3.f, f1) == 0.5f) 76 | 77 | // Fma 78 | ASSERT(fma(f1, 1.f, 0.5f) == 2.f) 79 | 80 | // Frexp 81 | int n = 0; 82 | ASSERT(frexp(8.f, n) == 0.5f) 83 | ASSERT(n == 4) 84 | 85 | // FrexpStruct TODO 86 | 87 | // Ldexp 88 | n = 4; 89 | ASSERT(ldexp(0.95f, n) == 15.2f) 90 | 91 | // PackSnorm4x8 92 | vec4 v1 = vec4(1.0f, 0.0f, -0.5f, -1.0f); 93 | u1 = 2176843903; 94 | ASSERT(packSnorm4x8(v1) == u1) 95 | 96 | // PackUnorm4x8 97 | vec4 v2 = vec4(1.0f, 0.5f, 0.0f, 1.0f); 98 | u2 = 4278223103; 99 | ASSERT(packUnorm4x8(v2) == u2) 100 | 101 | // PackSnorm2x16 102 | vec2 v3 = vec2(-0.5f, -0.7f); 103 | uint u3 = 2791817216; 104 | ASSERT(packSnorm2x16(v3) == u3) 105 | 106 | // PackUnorm2x16 107 | vec2 v4 = vec2(0.5f, 0.7f); 108 | uint u4 = 3006496768; 109 | ASSERT(packUnorm2x16(v4) == u4) 110 | 111 | // UnpackSnorm2x16 112 | // ASSERT(unpackSnorm2x16(u3) == v3) 113 | 114 | // UnpackUnorm2x16 115 | // ASSERT(unpackUnorm2x16(u4) == v4) 116 | 117 | // UnpackSnorm4x8 118 | // ASSERT(unpackSnorm4x8(u1) == v1) 119 | 120 | // UnpackUnorm4x8 121 | // ASSERT(unpackUnorm4x8(u2) == v2) 122 | 123 | // PackHalf2x16 TODO 124 | // UnpackHalf2x16 TODO 125 | // PackDouble2x32 TODO 126 | // UnpackDouble2x32 TODO 127 | 128 | ASSERT_END 129 | } 130 | -------------------------------------------------------------------------------- /test/assets/glsl_std_450_1.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/glsl_std_450_1.frag.spv -------------------------------------------------------------------------------- /test/assets/glsl_std_450_2.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | #define PI 3.1415926535f 7 | 8 | void main() 9 | { 10 | ASSERT_BEGIN(64) 11 | 12 | // Length 13 | vec2 v2 = vec2(1.f, 0.f); 14 | vec3 v3 = vec3(1.f, 0.f, 0.f); 15 | vec4 v4 = vec4(1.f, 2.f, 3.f, 4.f); 16 | ASSERT(length(v2) == 1.f); 17 | ASSERT(length(v3) == 1.f); 18 | ASSERT(length(v4) == 5.47722578f); 19 | 20 | // Distance 21 | ASSERT(distance(v2, vec2(1.f, 0.f)) == 0.f); 22 | ASSERT(distance(v3, vec3(1.f, 0.f, 1.f)) == 1.f); 23 | ASSERT(distance(v4, vec4(1.f, 0.f, 0.f, 0.f)) == 5.38516474f); 24 | 25 | // Cross 26 | ASSERT(cross(v3, vec3(0.f, 1.f, 0.f)) == vec3(0.f, 0.f, 1.f)); 27 | ASSERT(cross(vec3(0.f, 1.f, 0.f), v3) == vec3(0.f, 0.f, -1.f)); 28 | 29 | // Normalize 30 | v3 = vec3(1.f, 2.f, 3.f); 31 | ASSERT(normalize(v2) == vec2(1.f, 0.f)) 32 | ASSERT(normalize(v3) == vec3(0.267261237f, 0.534522474f, 0.801783681f)) 33 | 34 | // FaceForward 35 | vec3 N = vec3(0.0f, 0.0f, 1.0f); 36 | vec3 I = vec3(1.0f, 0.0f, 1.0f); 37 | vec3 Nref = vec3(0.0f, 0.0f, 1.0f); 38 | vec3 F = faceforward(N, I, Nref); 39 | ASSERT(F == vec3(0.f, 0.f, -1.f)); 40 | 41 | // Reflect 42 | vec2 A = vec2(1.0f, -1.0f); 43 | vec2 B = vec2(0.0f, 1.0f); 44 | vec2 C = reflect(A, B); 45 | ASSERT(C == vec2(1.f, 1.f)) 46 | 47 | // Refract 48 | A = vec2(0.0f, -1.0f); 49 | B = vec2(0.0f, 1.0f); 50 | C = refract(A, B, 0.5f); 51 | ASSERT(C == vec2(0.f, -1.f)) 52 | 53 | // FindILsb 54 | ASSERT(findLSB(0x00000001) == 0) 55 | ASSERT(findLSB(0x00000003) == 0) 56 | ASSERT(findLSB(0x00000002) == 1) 57 | ASSERT(findLSB(0x00010000) == 16) 58 | ASSERT(findLSB(0x7FFF0000) == 16) 59 | ASSERT(findLSB(0x7F000000) == 24) 60 | ASSERT(findLSB(0x7F00FF00) == 8) 61 | ASSERT(findLSB(0x00000000) == -1) 62 | 63 | // FindSMsb 64 | int a = 0x00000000; 65 | ASSERT(findMSB(a) == -1) 66 | a = 0x00000001; 67 | ASSERT(findMSB(a) == 0) 68 | a = 0x40000000; 69 | ASSERT(findMSB(a) == 30) 70 | a = -1; 71 | ASSERT(findMSB(a) == -1) 72 | 73 | ivec4 ia = ivec4(0x00000000); 74 | ASSERT(findMSB(ia) == ivec4(-1)) 75 | ia = ivec4(0x00000001); 76 | ASSERT(findMSB(ia) == ivec4(0)) 77 | ia = ivec4(0x40000000); 78 | ASSERT(findMSB(ia) == ivec4(30)) 79 | 80 | // FindUMsb 81 | uint b = 0x00000000; 82 | ASSERT(findMSB(b) == -1) 83 | b = 0x00000001; 84 | ASSERT(findMSB(b) == 0) 85 | b = 0x40000000; 86 | ASSERT(findMSB(b) == 30) 87 | 88 | // InterpolateAtCentroid TODO 89 | // InterpolateAtSample TODO 90 | // InterpolateAtOffset TODO 91 | 92 | // NMin TODO 93 | // NMax TODO 94 | // NClamp TODO 95 | 96 | ASSERT_END 97 | } 98 | -------------------------------------------------------------------------------- /test/assets/glsl_std_450_2.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/glsl_std_450_2.frag.spv -------------------------------------------------------------------------------- /test/assets/image.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform sampler2D texSampler; 4 | 5 | layout(location = 0) in vec2 fragTexCoord; 6 | layout(location = 1) flat in int inLod; 7 | layout(location = 2) flat in ivec2 inFetchCoord; 8 | layout(location = 3) in vec2 dPdx; 9 | layout(location = 4) in vec2 dPdy; 10 | 11 | layout(location = 0) out vec2 outQueryLod; 12 | layout(location = 1) out ivec2 outTexSize; 13 | layout(location = 2) out int outTexLevel; 14 | layout(location = 3) out vec4 outColorFetch; 15 | layout(location = 4) out vec4 outColorFetchOffset; 16 | layout(location = 5) out vec4 outColorSample; 17 | layout(location = 6) out vec4 outColorSampleGrad; 18 | layout(location = 7) out vec4 outColorSampleLod; 19 | layout(location = 8) out vec4 outColorSampleOffset; 20 | 21 | void main() { 22 | outQueryLod = textureQueryLod(texSampler, fragTexCoord); 23 | outTexSize = textureSize(texSampler, inLod); 24 | outTexLevel = textureQueryLevels(texSampler); 25 | 26 | outColorFetch = texelFetch(texSampler, inFetchCoord, inLod); 27 | outColorFetchOffset = texelFetchOffset(texSampler, inFetchCoord, inLod, ivec2(1, 1)); 28 | 29 | outColorSample = texture(texSampler, fragTexCoord); 30 | outColorSampleGrad = textureGrad(texSampler, fragTexCoord, dPdx, dPdy); 31 | outColorSampleLod = textureLod(texSampler, fragTexCoord, inLod); 32 | outColorSampleOffset = textureOffset(texSampler, fragTexCoord, ivec2(1, 0), 0.2f); 33 | } 34 | -------------------------------------------------------------------------------- /test/assets/image.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/image.frag.spv -------------------------------------------------------------------------------- /test/assets/image.frag.spv.txt: -------------------------------------------------------------------------------- 1 | image.frag 2 | // Module Version 10000 3 | // Generated by (magic number): 8000a 4 | // Id's are bound by 77 5 | 6 | Capability Shader 7 | Capability ImageQuery 8 | 1: ExtInstImport "GLSL.std.450" 9 | MemoryModel Logical GLSL450 10 | EntryPoint Fragment 4 "main" 9 16 22 25 30 36 39 44 52 56 59 61 64 70 11 | ExecutionMode 4 OriginUpperLeft 12 | Source GLSL 450 13 | Name 4 "main" 14 | Name 9 "outQueryLod" 15 | Name 13 "texSampler" 16 | Name 16 "fragTexCoord" 17 | Name 22 "outTexSize" 18 | Name 25 "inLod" 19 | Name 30 "outTexLevel" 20 | Name 36 "outColorFetch" 21 | Name 39 "inFetchCoord" 22 | Name 44 "outColorFetchOffset" 23 | Name 52 "outColorSample" 24 | Name 56 "outColorSampleGrad" 25 | Name 59 "dPdx" 26 | Name 61 "dPdy" 27 | Name 64 "outColorSampleLod" 28 | Name 70 "outColorSampleOffset" 29 | Decorate 9(outQueryLod) Location 0 30 | Decorate 13(texSampler) DescriptorSet 0 31 | Decorate 13(texSampler) Binding 0 32 | Decorate 16(fragTexCoord) Location 0 33 | Decorate 22(outTexSize) Location 1 34 | Decorate 25(inLod) Flat 35 | Decorate 25(inLod) Location 1 36 | Decorate 30(outTexLevel) Location 2 37 | Decorate 36(outColorFetch) Location 3 38 | Decorate 39(inFetchCoord) Flat 39 | Decorate 39(inFetchCoord) Location 2 40 | Decorate 44(outColorFetchOffset) Location 4 41 | Decorate 52(outColorSample) Location 5 42 | Decorate 56(outColorSampleGrad) Location 6 43 | Decorate 59(dPdx) Location 3 44 | Decorate 61(dPdy) Location 4 45 | Decorate 64(outColorSampleLod) Location 7 46 | Decorate 70(outColorSampleOffset) Location 8 47 | 2: TypeVoid 48 | 3: TypeFunction 2 49 | 6: TypeFloat 32 50 | 7: TypeVector 6(float) 2 51 | 8: TypePointer Output 7(fvec2) 52 | 9(outQueryLod): 8(ptr) Variable Output 53 | 10: TypeImage 6(float) 2D sampled format:Unknown 54 | 11: TypeSampledImage 10 55 | 12: TypePointer UniformConstant 11 56 | 13(texSampler): 12(ptr) Variable UniformConstant 57 | 15: TypePointer Input 7(fvec2) 58 | 16(fragTexCoord): 15(ptr) Variable Input 59 | 19: TypeInt 32 1 60 | 20: TypeVector 19(int) 2 61 | 21: TypePointer Output 20(ivec2) 62 | 22(outTexSize): 21(ptr) Variable Output 63 | 24: TypePointer Input 19(int) 64 | 25(inLod): 24(ptr) Variable Input 65 | 29: TypePointer Output 19(int) 66 | 30(outTexLevel): 29(ptr) Variable Output 67 | 34: TypeVector 6(float) 4 68 | 35: TypePointer Output 34(fvec4) 69 | 36(outColorFetch): 35(ptr) Variable Output 70 | 38: TypePointer Input 20(ivec2) 71 | 39(inFetchCoord): 38(ptr) Variable Input 72 | 44(outColorFetchOffset): 35(ptr) Variable Output 73 | 48: 19(int) Constant 1 74 | 49: 20(ivec2) ConstantComposite 48 48 75 | 52(outColorSample): 35(ptr) Variable Output 76 | 56(outColorSampleGrad): 35(ptr) Variable Output 77 | 59(dPdx): 15(ptr) Variable Input 78 | 61(dPdy): 15(ptr) Variable Input 79 | 64(outColorSampleLod): 35(ptr) Variable Output 80 | 70(outColorSampleOffset): 35(ptr) Variable Output 81 | 73: 19(int) Constant 0 82 | 74: 20(ivec2) ConstantComposite 48 73 83 | 75: 6(float) Constant 1045220557 84 | 4(main): 2 Function None 3 85 | 5: Label 86 | 14: 11 Load 13(texSampler) 87 | 17: 7(fvec2) Load 16(fragTexCoord) 88 | 18: 7(fvec2) ImageQueryLod 14 17 89 | Store 9(outQueryLod) 18 90 | 23: 11 Load 13(texSampler) 91 | 26: 19(int) Load 25(inLod) 92 | 27: 10 Image 23 93 | 28: 20(ivec2) ImageQuerySizeLod 27 26 94 | Store 22(outTexSize) 28 95 | 31: 11 Load 13(texSampler) 96 | 32: 10 Image 31 97 | 33: 19(int) ImageQueryLevels 32 98 | Store 30(outTexLevel) 33 99 | 37: 11 Load 13(texSampler) 100 | 40: 20(ivec2) Load 39(inFetchCoord) 101 | 41: 19(int) Load 25(inLod) 102 | 42: 10 Image 37 103 | 43: 34(fvec4) ImageFetch 42 40 Lod 41 104 | Store 36(outColorFetch) 43 105 | 45: 11 Load 13(texSampler) 106 | 46: 20(ivec2) Load 39(inFetchCoord) 107 | 47: 19(int) Load 25(inLod) 108 | 50: 10 Image 45 109 | 51: 34(fvec4) ImageFetch 50 46 Lod ConstOffset 47 49 110 | Store 44(outColorFetchOffset) 51 111 | 53: 11 Load 13(texSampler) 112 | 54: 7(fvec2) Load 16(fragTexCoord) 113 | 55: 34(fvec4) ImageSampleImplicitLod 53 54 114 | Store 52(outColorSample) 55 115 | 57: 11 Load 13(texSampler) 116 | 58: 7(fvec2) Load 16(fragTexCoord) 117 | 60: 7(fvec2) Load 59(dPdx) 118 | 62: 7(fvec2) Load 61(dPdy) 119 | 63: 34(fvec4) ImageSampleExplicitLod 57 58 Grad 60 62 120 | Store 56(outColorSampleGrad) 63 121 | 65: 11 Load 13(texSampler) 122 | 66: 7(fvec2) Load 16(fragTexCoord) 123 | 67: 19(int) Load 25(inLod) 124 | 68: 6(float) ConvertSToF 67 125 | 69: 34(fvec4) ImageSampleExplicitLod 65 66 Lod 68 126 | Store 64(outColorSampleLod) 69 127 | 71: 11 Load 13(texSampler) 128 | 72: 7(fvec2) Load 16(fragTexCoord) 129 | 76: 34(fvec4) ImageSampleImplicitLod 71 72 Bias ConstOffset 75 74 130 | Store 70(outColorSampleOffset) 76 131 | Return 132 | FunctionEnd 133 | -------------------------------------------------------------------------------- /test/assets/location.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout (location = 0) in vec3 inColor; 4 | 5 | layout (location = 0) out vec4 outFragColor; 6 | 7 | void main() 8 | { 9 | outFragColor = vec4(inColor.yxz, 1.0f); 10 | } 11 | -------------------------------------------------------------------------------- /test/assets/location.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/location.frag.spv -------------------------------------------------------------------------------- /test/assets/location.frag.spv.txt: -------------------------------------------------------------------------------- 1 | .\location.frag 2 | // Module Version 10000 3 | // Generated by (magic number): 8000a 4 | // Id's are bound by 20 5 | 6 | Capability Shader 7 | 1: ExtInstImport "GLSL.std.450" 8 | MemoryModel Logical GLSL450 9 | EntryPoint Fragment 4 "main" 9 12 10 | ExecutionMode 4 OriginUpperLeft 11 | Source GLSL 450 12 | Name 4 "main" 13 | Name 9 "outFragColor" 14 | Name 12 "inColor" 15 | Decorate 9(outFragColor) Location 0 16 | Decorate 12(inColor) Location 0 17 | 2: TypeVoid 18 | 3: TypeFunction 2 19 | 6: TypeFloat 32 20 | 7: TypeVector 6(float) 4 21 | 8: TypePointer Output 7(fvec4) 22 | 9(outFragColor): 8(ptr) Variable Output 23 | 10: TypeVector 6(float) 3 24 | 11: TypePointer Input 10(fvec3) 25 | 12(inColor): 11(ptr) Variable Input 26 | 15: 6(float) Constant 1065353216 27 | 4(main): 2 Function None 3 28 | 5: Label 29 | 13: 10(fvec3) Load 12(inColor) 30 | 14: 10(fvec3) VectorShuffle 13 13 1 0 2 31 | 16: 6(float) CompositeExtract 14 0 32 | 17: 6(float) CompositeExtract 14 1 33 | 18: 6(float) CompositeExtract 14 2 34 | 19: 7(fvec4) CompositeConstruct 16 17 18 15 35 | Store 9(outFragColor) 19 36 | Return 37 | FunctionEnd 38 | -------------------------------------------------------------------------------- /test/assets/relational_logical.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_GOOGLE_include_directive : require 4 | #include "assert.glsl" 5 | 6 | void main() 7 | { 8 | ASSERT_BEGIN(64) 9 | 10 | bvec2 a = bvec2(true, false); 11 | bvec2 b = bvec2(false, false); 12 | bvec2 c = bvec2(true, true); 13 | bvec2 d = bvec2(false, true); 14 | 15 | // SpvOpAny 16 | ASSERT(any(a)) 17 | ASSERT(!any(b)) 18 | 19 | // SpvOpAll 20 | ASSERT(!all(a)) 21 | ASSERT(!all(b)) 22 | ASSERT(all(c)) 23 | 24 | float fNan = intBitsToFloat(0x7ff80000); // nan 25 | float fInf = 10.f / 0.f; // inf 26 | vec3 vf = vec3(1.f, fNan, fInf); 27 | 28 | // SpvOpIsNan 29 | ASSERT(isnan(fNan)) 30 | ASSERT(isnan(vf) == bvec3(false, true, false)) 31 | 32 | // SpvOpIsInf 33 | ASSERT(isinf(fInf)) 34 | ASSERT(isinf(vf) == bvec3(false, false, true)) 35 | 36 | // SpvOpLogicalEqual 37 | ASSERT(c == bvec2(true)) 38 | 39 | // SpvOpLogicalNotEqual 40 | ASSERT(c != bvec2(false)) 41 | 42 | // SpvOpLogicalOr 43 | bool b1 = true; 44 | bool b2 = false; 45 | ASSERT((b1 || b2)) 46 | 47 | // SpvOpLogicalAnd 48 | ASSERT(!(b1 && b2)) 49 | 50 | // SpvOpLogicalNot 51 | ASSERT(not(a) == d) 52 | ASSERT(not(b) == c) 53 | 54 | // SpvOpSelect 55 | ASSERT((b1 ? 0.1f : 0.2f) == 0.1f) 56 | ASSERT((b2 ? 0.1f : 0.2f) == 0.2f) 57 | 58 | 59 | int i1 = -1; 60 | int i2 = -2; 61 | uint u1 = 1; 62 | uint u2 = 2; 63 | 64 | // SpvOpIEqual 65 | ASSERT(i1 - 1 == i2) 66 | 67 | // SpvOpINotEqual 68 | ASSERT(i1 - 2 != i2) 69 | 70 | // SpvOpUGreaterThan 71 | ASSERT(u2 > u1) 72 | 73 | // SpvOpSGreaterThan 74 | ASSERT(i1 > i2) 75 | 76 | // SpvOpUGreaterThanEqual 77 | ASSERT(u2 >= u1) 78 | 79 | // SpvOpSGreaterThanEqual 80 | ASSERT(i1 >= i2) 81 | 82 | // SpvOpULessThan 83 | ASSERT(u1 < u2) 84 | 85 | // SpvOpSLessThan 86 | ASSERT(i2 < i1) 87 | 88 | // SpvOpULessThanEqual 89 | ASSERT(u1 <= u2) 90 | 91 | // SpvOpSLessThanEqual 92 | ASSERT(i2 <= i1) 93 | 94 | 95 | float f1 = 1.f; 96 | float f2 = 2.f; 97 | 98 | // SpvOpFOrdEqual 99 | ASSERT(f1 + 1.f == f2) 100 | 101 | // SpvOpFOrdNotEqual TODO 102 | 103 | // SpvOpFOrdLessThan 104 | ASSERT(f1 < f2) 105 | 106 | // SpvOpFOrdGreaterThan 107 | ASSERT(f2 > f1) 108 | 109 | // SpvOpFOrdLessThanEqual 110 | ASSERT(f1 <= f2) 111 | 112 | // SpvOpFOrdGreaterThanEqual 113 | ASSERT(f2 >= f1) 114 | 115 | // SpvOpFUnordEqual 116 | 117 | // SpvOpFUnordNotEqual 118 | ASSERT(f1 != f2) 119 | 120 | // SpvOpFUnordLessThan TODO 121 | // SpvOpFUnordGreaterThan TODO 122 | // SpvOpFUnordLessThanEqual TODO 123 | // SpvOpFUnordGreaterThanEqual TODO 124 | 125 | ASSERT_END 126 | } 127 | -------------------------------------------------------------------------------- /test/assets/relational_logical.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/relational_logical.frag.spv -------------------------------------------------------------------------------- /test/assets/uniform_block.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout (location = 0) in vec3 vPosition; 3 | 4 | layout(set = 0, binding = 0) uniform CameraBuffer 5 | { 6 | vec4 data; 7 | mat4 viewProj; 8 | } cameraData; 9 | 10 | layout(push_constant) uniform constants 11 | { 12 | vec4 data; 13 | mat4 renderMatrix; 14 | } PushConstants; 15 | 16 | void main() 17 | { 18 | mat4 transformMatrix = (cameraData.viewProj * PushConstants.renderMatrix); 19 | gl_Position = transformMatrix * vec4(vPosition, 1.0f); 20 | } 21 | -------------------------------------------------------------------------------- /test/assets/uniform_block.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keith2018/spvm/a232ea822562dde8d9de47be458a3777392d98bb/test/assets/uniform_block.vert.spv -------------------------------------------------------------------------------- /test/assets/uniform_block.vert.spv.txt: -------------------------------------------------------------------------------- 1 | .\uniform_block.vert 2 | // Module Version 10000 3 | // Generated by (magic number): 8000a 4 | // Id's are bound by 46 5 | 6 | Capability Shader 7 | 1: ExtInstImport "GLSL.std.450" 8 | MemoryModel Logical GLSL450 9 | EntryPoint Vertex 4 "main" 31 36 10 | Source GLSL 450 11 | Name 4 "main" 12 | Name 10 "transformMatrix" 13 | Name 11 "CameraBuffer" 14 | MemberName 11(CameraBuffer) 0 "data" 15 | MemberName 11(CameraBuffer) 1 "viewProj" 16 | Name 13 "cameraData" 17 | Name 19 "constants" 18 | MemberName 19(constants) 0 "data" 19 | MemberName 19(constants) 1 "renderMatrix" 20 | Name 21 "PushConstants" 21 | Name 29 "gl_PerVertex" 22 | MemberName 29(gl_PerVertex) 0 "gl_Position" 23 | MemberName 29(gl_PerVertex) 1 "gl_PointSize" 24 | MemberName 29(gl_PerVertex) 2 "gl_ClipDistance" 25 | MemberName 29(gl_PerVertex) 3 "gl_CullDistance" 26 | Name 31 "" 27 | Name 36 "vPosition" 28 | MemberDecorate 11(CameraBuffer) 0 Offset 0 29 | MemberDecorate 11(CameraBuffer) 1 ColMajor 30 | MemberDecorate 11(CameraBuffer) 1 Offset 16 31 | MemberDecorate 11(CameraBuffer) 1 MatrixStride 16 32 | Decorate 11(CameraBuffer) Block 33 | Decorate 13(cameraData) DescriptorSet 0 34 | Decorate 13(cameraData) Binding 0 35 | MemberDecorate 19(constants) 0 Offset 0 36 | MemberDecorate 19(constants) 1 ColMajor 37 | MemberDecorate 19(constants) 1 Offset 16 38 | MemberDecorate 19(constants) 1 MatrixStride 16 39 | Decorate 19(constants) Block 40 | MemberDecorate 29(gl_PerVertex) 0 BuiltIn Position 41 | MemberDecorate 29(gl_PerVertex) 1 BuiltIn PointSize 42 | MemberDecorate 29(gl_PerVertex) 2 BuiltIn ClipDistance 43 | MemberDecorate 29(gl_PerVertex) 3 BuiltIn CullDistance 44 | Decorate 29(gl_PerVertex) Block 45 | Decorate 36(vPosition) Location 0 46 | 2: TypeVoid 47 | 3: TypeFunction 2 48 | 6: TypeFloat 32 49 | 7: TypeVector 6(float) 4 50 | 8: TypeMatrix 7(fvec4) 4 51 | 9: TypePointer Function 8 52 | 11(CameraBuffer): TypeStruct 7(fvec4) 8 53 | 12: TypePointer Uniform 11(CameraBuffer) 54 | 13(cameraData): 12(ptr) Variable Uniform 55 | 14: TypeInt 32 1 56 | 15: 14(int) Constant 1 57 | 16: TypePointer Uniform 8 58 | 19(constants): TypeStruct 7(fvec4) 8 59 | 20: TypePointer PushConstant 19(constants) 60 | 21(PushConstants): 20(ptr) Variable PushConstant 61 | 22: TypePointer PushConstant 8 62 | 26: TypeInt 32 0 63 | 27: 26(int) Constant 1 64 | 28: TypeArray 6(float) 27 65 | 29(gl_PerVertex): TypeStruct 7(fvec4) 6(float) 28 28 66 | 30: TypePointer Output 29(gl_PerVertex) 67 | 31: 30(ptr) Variable Output 68 | 32: 14(int) Constant 0 69 | 34: TypeVector 6(float) 3 70 | 35: TypePointer Input 34(fvec3) 71 | 36(vPosition): 35(ptr) Variable Input 72 | 38: 6(float) Constant 1065353216 73 | 44: TypePointer Output 7(fvec4) 74 | 4(main): 2 Function None 3 75 | 5: Label 76 | 10(transformMatrix): 9(ptr) Variable Function 77 | 17: 16(ptr) AccessChain 13(cameraData) 15 78 | 18: 8 Load 17 79 | 23: 22(ptr) AccessChain 21(PushConstants) 15 80 | 24: 8 Load 23 81 | 25: 8 MatrixTimesMatrix 18 24 82 | Store 10(transformMatrix) 25 83 | 33: 8 Load 10(transformMatrix) 84 | 37: 34(fvec3) Load 36(vPosition) 85 | 39: 6(float) CompositeExtract 37 0 86 | 40: 6(float) CompositeExtract 37 1 87 | 41: 6(float) CompositeExtract 37 2 88 | 42: 7(fvec4) CompositeConstruct 39 40 41 38 89 | 43: 7(fvec4) MatrixTimesVector 33 42 90 | 45: 44(ptr) AccessChain 31 32 91 | Store 45 43 92 | Return 93 | FunctionEnd 94 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "gtest/gtest.h" 8 | 9 | int main(int argc, char *argv[]) { 10 | testing::InitGoogleTest(&argc, argv); 11 | return RUN_ALL_TESTS(); 12 | } 13 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "gtest/gtest.h" 10 | 11 | #include "decoder.h" 12 | #include "runtime.h" 13 | #include "image.h" 14 | 15 | #define HEAP_SIZE 128 * 1024 16 | 17 | typedef struct TestContext_ { 18 | SPVM::SpvmModule module; 19 | SPVM::Runtime runtime; 20 | 21 | bool decode(const char *path) { 22 | return SPVM::Decoder::decodeFile(path, &module); 23 | } 24 | 25 | bool init() { 26 | return runtime.initWithModule(&module, HEAP_SIZE); 27 | } 28 | 29 | } TestContext; 30 | 31 | #define ASSERT_VEC2_EQ(vec, v0, v1) \ 32 | ASSERT_EQ((vec)[0], v0); \ 33 | ASSERT_EQ((vec)[1], v1); 34 | 35 | #define ASSERT_VEC3_EQ(vec, v0, v1, v2) \ 36 | ASSERT_EQ((vec)[0], v0); \ 37 | ASSERT_EQ((vec)[1], v1); \ 38 | ASSERT_EQ((vec)[2], v2); 39 | 40 | #define ASSERT_VEC4_EQ(vec, v0, v1, v2, v3) \ 41 | ASSERT_EQ((vec)[0], v0); \ 42 | ASSERT_EQ((vec)[1], v1); \ 43 | ASSERT_EQ((vec)[2], v2); \ 44 | ASSERT_EQ((vec)[3], v3); 45 | 46 | #define ASSERT_FLOAT_VEC3_EQ(vec, v0, v1, v2) \ 47 | ASSERT_FLOAT_EQ((vec)[0], v0); \ 48 | ASSERT_FLOAT_EQ((vec)[1], v1); \ 49 | ASSERT_FLOAT_EQ((vec)[2], v2); 50 | 51 | #define ASSERT_FLOAT_VEC4_EQ(vec, v0, v1, v2, v3) \ 52 | ASSERT_FLOAT_EQ((vec)[0], v0); \ 53 | ASSERT_FLOAT_EQ((vec)[1], v1); \ 54 | ASSERT_FLOAT_EQ((vec)[2], v2); \ 55 | ASSERT_FLOAT_EQ((vec)[3], v3); 56 | 57 | 58 | #define TEST_GLSL_SPV(name, path) \ 59 | TEST(TEST_SPV_EXEC, name) { \ 60 | TestContext ctx; \ 61 | ASSERT_TRUE(ctx.decode(path)); \ 62 | ASSERT_TRUE(ctx.init()); \ 63 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); \ 64 | int outAssert = -1; \ 65 | ctx.runtime.readOutput(&outAssert, 0); \ 66 | ASSERT_FLOAT_EQ(outAssert, -1); \ 67 | } 68 | -------------------------------------------------------------------------------- /test/test_core.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "test.h" 8 | 9 | TEST(TEST_SPV_EXEC, location_frag) { 10 | TestContext ctx; 11 | ASSERT_TRUE(ctx.decode("assets/location.frag.spv")); 12 | ASSERT_TRUE(ctx.init()); 13 | 14 | SPVM::SpvmWord inColorIdx = ctx.runtime.getLocationByName("inColor"); 15 | SPVM::SpvmWord outColorIdx = ctx.runtime.getLocationByName("outFragColor"); 16 | 17 | ASSERT_EQ(inColorIdx, 0); 18 | ASSERT_EQ(outColorIdx, 0); 19 | 20 | float inColor[3]{0.2f, 0.3f, 0.4f}; 21 | float outFragColor[4]; 22 | 23 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 24 | ctx.runtime.readOutput(outFragColor, outColorIdx); 25 | ASSERT_FLOAT_VEC4_EQ(outFragColor, 0.f, 0.f, 0.f, 1.f) 26 | 27 | ctx.runtime.writeInput(inColor, inColorIdx); 28 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 29 | ctx.runtime.readOutput(outFragColor, outColorIdx); 30 | ASSERT_FLOAT_VEC4_EQ(outFragColor, 0.3f, 0.2f, 0.4f, 1.f) 31 | } 32 | 33 | TEST(TEST_SPV_EXEC, built_in_vert) { 34 | TestContext ctx; 35 | ASSERT_TRUE(ctx.decode("assets/built_in.vert.spv")); 36 | ASSERT_TRUE(ctx.init()); 37 | 38 | int inVertexIndex = 0; 39 | float outBuiltInPosition[4]; 40 | float outFragColor[3]; 41 | 42 | // index 0 43 | inVertexIndex = 0; 44 | ctx.runtime.writeInputBuiltIn(&inVertexIndex, SpvBuiltInVertexIndex); 45 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 46 | ctx.runtime.readOutputBuiltIn(outBuiltInPosition, SpvBuiltInPosition); 47 | ctx.runtime.readOutput(outFragColor, 0); 48 | ASSERT_FLOAT_VEC4_EQ(outBuiltInPosition, 1.f, 1.f, 0.f, 1.f) 49 | ASSERT_FLOAT_VEC3_EQ(outFragColor, 1.f, 0.f, 0.f) 50 | 51 | // index 1 52 | inVertexIndex = 1; 53 | ctx.runtime.writeInputBuiltIn(&inVertexIndex, SpvBuiltInVertexIndex); 54 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 55 | ctx.runtime.readOutputBuiltIn(outBuiltInPosition, SpvBuiltInPosition); 56 | ctx.runtime.readOutput(outFragColor, 0); 57 | ASSERT_FLOAT_VEC4_EQ(outBuiltInPosition, -1.f, 1.f, 0.f, 1.f) 58 | ASSERT_FLOAT_VEC3_EQ(outFragColor, 0.f, 1.f, 0.f) 59 | 60 | // index 2 61 | inVertexIndex = 2; 62 | ctx.runtime.writeInputBuiltIn(&inVertexIndex, SpvBuiltInVertexIndex); 63 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 64 | ctx.runtime.readOutputBuiltIn(outBuiltInPosition, SpvBuiltInPosition); 65 | ctx.runtime.readOutput(outFragColor, 0); 66 | ASSERT_FLOAT_VEC4_EQ(outBuiltInPosition, 0.f, -1.f, 0.f, 1.f) 67 | ASSERT_FLOAT_VEC3_EQ(outFragColor, 0.f, 0.f, 1.f) 68 | } 69 | 70 | TEST(TEST_SPV_EXEC, uniform_block_vert) { 71 | TestContext ctx; 72 | ASSERT_TRUE(ctx.decode("assets/uniform_block.vert.spv")); 73 | ASSERT_TRUE(ctx.init()); 74 | 75 | SPVM::SpvmWord vPositionIdx = ctx.runtime.getLocationByName("vPosition"); 76 | SPVM::SpvmUniformBinding cameraDataIdx = ctx.runtime.getBindingByName("cameraData"); 77 | 78 | ASSERT_EQ(vPositionIdx, 0); 79 | ASSERT_EQ(cameraDataIdx.binding, 0); 80 | ASSERT_EQ(cameraDataIdx.set, 0); 81 | 82 | float vPosition[3]{0.2f, 0.3f, 0.4f}; 83 | struct { 84 | float data[4] = {0.f, 0.f, 0.f, 0.f}; 85 | float viewProj[16] = { 86 | 1.f, 1.f, 0.f, 0.f, 87 | 0.f, 1.f, 0.f, 0.f, 88 | 0.f, 0.f, 1.f, 0.f, 89 | 0.f, 0.f, 0.f, 1.f 90 | }; 91 | } cameraData; 92 | struct { 93 | float data[4] = {0.f, 0.f, 0.f, 0.f}; 94 | float renderMatrix[16] = { 95 | 1.f, 2.f, 0.f, 0.f, 96 | 0.f, 1.f, 0.f, 0.f, 97 | 0.f, 0.f, 1.f, 0.f, 98 | 0.f, 0.f, 0.f, 1.f 99 | }; 100 | } pushConstants; 101 | float outBuiltInPosition[4]; 102 | 103 | ctx.runtime.writeInput(vPosition, vPositionIdx); 104 | ctx.runtime.writeUniformBinding(&cameraData, sizeof(cameraData), 105 | cameraDataIdx.binding, cameraDataIdx.set); 106 | ctx.runtime.writeUniformPushConstants(&pushConstants, sizeof(pushConstants)); 107 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 108 | ctx.runtime.readOutputBuiltIn(outBuiltInPosition, SpvBuiltInPosition); 109 | ASSERT_FLOAT_VEC4_EQ(outBuiltInPosition, 0.2f, 0.9f, 0.4f, 1.f) 110 | } 111 | 112 | TEST(TEST_SPV_EXEC, array_frag) { 113 | TestContext ctx; 114 | ASSERT_TRUE(ctx.decode("assets/array.frag.spv")); 115 | ASSERT_TRUE(ctx.init()); 116 | 117 | struct { 118 | float a[3] = {4.2f, 5.0f, 5.2f}; 119 | float b[6] = {1.2f, 1.0f, 1.3f, 0.2f, 0.3f, 0.5f}; 120 | } tb; 121 | ctx.runtime.writeUniformBinding(&tb, sizeof(tb), 0, 0); 122 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 123 | 124 | int outAssert = -1; 125 | ctx.runtime.readOutput(&outAssert, 0); 126 | ASSERT_FLOAT_EQ(outAssert, -1); 127 | } 128 | 129 | TEST_GLSL_SPV(control_flow_frag, "assets/control_flow.frag.spv") 130 | TEST_GLSL_SPV(arithmetic_0_frag, "assets/arithmetic_0.frag.spv") 131 | TEST_GLSL_SPV(arithmetic_1_frag, "assets/arithmetic_1.frag.spv") 132 | TEST_GLSL_SPV(function_frag, "assets/function.frag.spv") 133 | TEST_GLSL_SPV(conversion_frag, "assets/conversion.frag.spv") 134 | TEST_GLSL_SPV(composite_frag, "assets/composite.frag.spv") 135 | TEST_GLSL_SPV(bit_frag, "assets/bit.frag.spv") 136 | TEST_GLSL_SPV(relational_logical, "assets/relational_logical.frag.spv") 137 | -------------------------------------------------------------------------------- /test/test_ext.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "test.h" 8 | 9 | TEST_GLSL_SPV(glsl_std_450_0, "assets/glsl_std_450_0.frag.spv") 10 | TEST_GLSL_SPV(glsl_std_450_1, "assets/glsl_std_450_1.frag.spv") 11 | TEST_GLSL_SPV(glsl_std_450_2, "assets/glsl_std_450_2.frag.spv") 12 | -------------------------------------------------------------------------------- /test/test_image.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * spvm 3 | * @author : keith@robot9.me 4 | * 5 | */ 6 | 7 | #include "test.h" 8 | 9 | typedef struct ImageContext_ { 10 | SPVM::SpvmImage *spvmImage; 11 | SPVM::SpvmSampler *spvmSampler; 12 | SPVM::SpvmSampledImage *spvmSampledImage; 13 | 14 | void create2d(int32_t width, int32_t height, int32_t mipLevels = 1) { 15 | SPVM::SpvmImageInfo imageInfo; 16 | imageInfo.dim = SpvDim2D; 17 | imageInfo.format = SPVM::SPVM_FORMAT_R8G8B8A8_UNORM; 18 | imageInfo.width = width; 19 | imageInfo.height = height; 20 | imageInfo.depth = 1; 21 | imageInfo.mipmaps = mipLevels > 1; 22 | imageInfo.baseMipLevel = 0; 23 | imageInfo.mipLevels = mipLevels; 24 | imageInfo.arrayed = false; 25 | imageInfo.baseArrayLayer = 0; 26 | imageInfo.arrayLayers = 1; 27 | imageInfo.samples = 1; 28 | 29 | spvmImage = SPVM::createImage(&imageInfo); 30 | spvmSampler = SPVM::createSampler(); 31 | spvmSampledImage = SPVM::createSampledImage(spvmImage, spvmSampler); 32 | } 33 | 34 | void destroy() { 35 | SPVM::destroyImage(spvmImage); 36 | SPVM::destroySampler(spvmSampler); 37 | SPVM::destroySampledImager(spvmSampledImage); 38 | } 39 | } ImageContext; 40 | 41 | static uint8_t pixelBytes2D[] = {255, 0, 0, 255, 0, 255, 0, 255, 42 | 0, 0, 255, 255, 0, 0, 0, 255, 43 | 255, 0, 0, 255, 255, 0, 0, 255, 44 | 255, 0, 0, 255, 255, 0, 0, 255, 45 | 255, 0, 0, 255, 255, 0, 0, 255, 46 | 255, 0, 0, 255, 255, 0, 0, 255, 47 | 255, 0, 0, 255, 255, 0, 0, 255, 48 | 255, 0, 0, 255, 255, 0, 0, 255,}; 49 | 50 | static uint8_t pixelBytes2D_mip[] = {128, 0, 0, 128, 0, 128, 0, 128, 51 | 0, 0, 128, 255, 0, 0, 0, 128,}; 52 | 53 | TEST(TEST_SPV_EXEC, image_2d_query_frag) { 54 | TestContext ctx; 55 | ASSERT_TRUE(ctx.decode("assets/image.frag.spv")); 56 | ASSERT_TRUE(ctx.init()); 57 | 58 | ImageContext imageCtx; 59 | imageCtx.create2d(4, 4, 2); 60 | imageCtx.spvmSampler->info.unnormalizedCoordinates = true; 61 | imageCtx.spvmSampler->info.mipmapMode = SpvSamplerFilterModeLinear; 62 | SPVM::uploadImageData(imageCtx.spvmImage, pixelBytes2D, sizeof(pixelBytes2D), 4, 4, 1, 0); 63 | SPVM::uploadImageData(imageCtx.spvmImage, pixelBytes2D_mip, sizeof(pixelBytes2D_mip), 2, 2, 1, 1); 64 | ctx.runtime.writeUniformBinding(imageCtx.spvmSampledImage, 0, 0, 0); 65 | 66 | SPVM::SpvmWord outQueryLodLoc = ctx.runtime.getLocationByName("outQueryLod"); 67 | SPVM::SpvmWord outTexSizeLoc = ctx.runtime.getLocationByName("outTexSize"); 68 | SPVM::SpvmWord outTexLevelLoc = ctx.runtime.getLocationByName("outTexLevel"); 69 | 70 | // textureQueryLod 71 | float fragTexCoord[2]{0.f, 0.f}; 72 | float outQueryLod[2]; 73 | ctx.runtime.writeInput(fragTexCoord, 0); 74 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 75 | ctx.runtime.readOutput(&outQueryLod, outQueryLodLoc); 76 | ASSERT_VEC2_EQ(outQueryLod, 0.f, 0.f); 77 | 78 | // textureSize 79 | int32_t inLod[1]{0}; 80 | int32_t outTexSize[2]; 81 | ctx.runtime.writeInput(inLod, 1); 82 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 83 | ctx.runtime.readOutput(&outTexSize, outTexSizeLoc); 84 | ASSERT_VEC2_EQ(outTexSize, 4, 4); 85 | 86 | inLod[0] = 1; 87 | ctx.runtime.writeInput(inLod, 1); 88 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 89 | ctx.runtime.readOutput(&outTexSize, outTexSizeLoc); 90 | ASSERT_VEC2_EQ(outTexSize, 2, 2); 91 | 92 | // textureQueryLevels 93 | int32_t outTexLevel[1]; 94 | ctx.runtime.readOutput(&outTexLevel, outTexLevelLoc); 95 | ASSERT_EQ(outTexLevel[0], 2); 96 | 97 | imageCtx.destroy(); 98 | } 99 | 100 | TEST(TEST_SPV_EXEC, image_2d_fetch_frag) { 101 | TestContext ctx; 102 | ASSERT_TRUE(ctx.decode("assets/image.frag.spv")); 103 | ASSERT_TRUE(ctx.init()); 104 | 105 | ImageContext imageCtx; 106 | imageCtx.create2d(4, 4, 2); 107 | imageCtx.spvmSampler->info.unnormalizedCoordinates = true; 108 | imageCtx.spvmSampler->info.mipmapMode = SpvSamplerFilterModeLinear; 109 | SPVM::uploadImageData(imageCtx.spvmImage, pixelBytes2D, sizeof(pixelBytes2D), 4, 4, 1, 0); 110 | SPVM::uploadImageData(imageCtx.spvmImage, pixelBytes2D_mip, sizeof(pixelBytes2D_mip), 2, 2, 1, 1); 111 | ctx.runtime.writeUniformBinding(imageCtx.spvmSampledImage, 0, 0, 0); 112 | 113 | SPVM::SpvmWord outColorFetchLoc = ctx.runtime.getLocationByName("outColorFetch"); 114 | SPVM::SpvmWord outColorFetchOffsetLoc = ctx.runtime.getLocationByName("outColorFetchOffset"); 115 | 116 | int32_t inLod[1]{1}; 117 | int32_t fragTexCoord[2]{0, 0}; 118 | 119 | // texelFetch 120 | ctx.runtime.writeInput(fragTexCoord, 0); 121 | ctx.runtime.writeInput(inLod, 1); 122 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 123 | float outColorFetch[4]; 124 | ctx.runtime.readOutput(&outColorFetch, outColorFetchLoc); 125 | ASSERT_VEC4_EQ(outColorFetch, 128.f / 255.f, 0.f, 0.f, 128.f / 255.f); 126 | 127 | // texelFetchOffset 128 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 129 | float outColorFetchOffset[4]; 130 | ctx.runtime.readOutput(&outColorFetchOffset, outColorFetchOffsetLoc); 131 | ASSERT_VEC4_EQ(outColorFetchOffset, 0.f, 0.f, 0.f, 128.f / 255.f); 132 | 133 | imageCtx.destroy(); 134 | } 135 | 136 | TEST(TEST_SPV_EXEC, image_2d_lookup_frag) { 137 | TestContext ctx; 138 | ASSERT_TRUE(ctx.decode("assets/image.frag.spv")); 139 | ASSERT_TRUE(ctx.init()); 140 | 141 | ImageContext imageCtx; 142 | imageCtx.create2d(4, 4, 2); 143 | imageCtx.spvmSampler->info.unnormalizedCoordinates = true; 144 | imageCtx.spvmSampler->info.mipmapMode = SpvSamplerFilterModeLinear; 145 | SPVM::uploadImageData(imageCtx.spvmImage, pixelBytes2D, sizeof(pixelBytes2D), 4, 4, 1, 0); 146 | SPVM::uploadImageData(imageCtx.spvmImage, pixelBytes2D_mip, sizeof(pixelBytes2D_mip), 2, 2, 1, 1); 147 | ctx.runtime.writeUniformBinding(imageCtx.spvmSampledImage, 0, 0, 0); 148 | 149 | float fragTexCoord[2]{0.f, 0.f}; 150 | SPVM::SpvmWord outLoc = ctx.runtime.getLocationByName("outColorSample"); 151 | 152 | // texture 153 | float outColorSample[4]; 154 | ctx.runtime.writeInput(fragTexCoord, 0); 155 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 156 | ctx.runtime.readOutput(&outColorSample, outLoc); 157 | ASSERT_VEC4_EQ(outColorSample, 1.f, 0.f, 0.f, 1.f); 158 | 159 | fragTexCoord[0] = 1.f; 160 | ctx.runtime.writeInput(fragTexCoord, 0); 161 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 162 | ctx.runtime.readOutput(&outColorSample, outLoc); 163 | ASSERT_VEC4_EQ(outColorSample, 0.f, 1.f, 0.f, 1.f); 164 | 165 | fragTexCoord[0] = 5.f; 166 | ctx.runtime.writeInput(fragTexCoord, 0); 167 | imageCtx.spvmSampler->info.addressModeU = SpvSamplerAddressingModeClampToEdge; 168 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 169 | ctx.runtime.readOutput(&outColorSample, outLoc); 170 | ASSERT_VEC4_EQ(outColorSample, 0.f, 0.f, 0.f, 1.f); 171 | 172 | fragTexCoord[0] = 5.f; 173 | ctx.runtime.writeInput(fragTexCoord, 0); 174 | imageCtx.spvmSampler->info.addressModeU = SpvSamplerAddressingModeClamp; 175 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 176 | ctx.runtime.readOutput(&outColorSample, outLoc); 177 | ASSERT_VEC4_EQ(outColorSample, 0.f, 0.f, 0.f, 0.f); 178 | 179 | fragTexCoord[0] = 5.f; 180 | ctx.runtime.writeInput(fragTexCoord, 0); 181 | imageCtx.spvmSampler->info.addressModeU = SpvSamplerAddressingModeRepeat; 182 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 183 | ctx.runtime.readOutput(&outColorSample, outLoc); 184 | ASSERT_VEC4_EQ(outColorSample, 0.f, 1.f, 0.f, 1.f); 185 | 186 | fragTexCoord[0] = 5.f; 187 | ctx.runtime.writeInput(fragTexCoord, 0); 188 | imageCtx.spvmSampler->info.addressModeU = SpvSamplerAddressingModeRepeatMirrored; 189 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 190 | ctx.runtime.readOutput(&outColorSample, outLoc); 191 | ASSERT_VEC4_EQ(outColorSample, 0.f, 0.f, 1.f, 1.f); 192 | 193 | // textureGrad TODO 194 | 195 | // textureLod 196 | int32_t inLod[1]{1}; 197 | fragTexCoord[0] = 0.f; 198 | fragTexCoord[1] = 0.f; 199 | ctx.runtime.writeInput(inLod, 1); 200 | ctx.runtime.writeInput(fragTexCoord, 0); 201 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 202 | outLoc = ctx.runtime.getLocationByName("outColorSampleLod"); 203 | float outColorSampleLod[4]; 204 | ctx.runtime.readOutput(&outColorSampleLod, outLoc); 205 | ASSERT_VEC4_EQ(outColorSampleLod, 128.f / 255.f, 0.f, 0.f, 128.f / 255.f); 206 | 207 | // textureOffset 208 | fragTexCoord[0] = 0.f; 209 | fragTexCoord[1] = 0.f; 210 | ctx.runtime.writeInput(fragTexCoord, 0); 211 | ASSERT_TRUE(ctx.runtime.execEntryPoint()); 212 | outLoc = ctx.runtime.getLocationByName("outColorSampleOffset"); 213 | float outColorSampleOffset[4]; 214 | ctx.runtime.readOutput(&outColorSampleOffset, outLoc); 215 | ASSERT_VEC4_EQ(outColorSampleOffset, 0.f, 1.f, 0.f, 1.f); 216 | 217 | imageCtx.destroy(); 218 | } -------------------------------------------------------------------------------- /test/tools/glsl.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | 3 | glslangValidator_name = 'glslangValidator' 4 | if sys.platform == 'win32': 5 | glslangValidator_name = 'glslangValidator.exe' 6 | 7 | 8 | def glsl_cvt(filename): 9 | cmd = "%s -H -V %s -o %s.spv > %s.spv.txt" % (glslangValidator_name, filename, filename, filename) 10 | os.system(cmd) 11 | 12 | 13 | if __name__ == "__main__": 14 | glsl_cvt(sys.argv[1]) --------------------------------------------------------------------------------